package src.glapp;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.lang.reflect.Method;
import java.nio.*;
import java.io.*;
import java.net.URL;
import org.lwjgl.*;
import org.lwjgl.opengl.*;
import org.lwjgl.input.*;
import org.lwjgl.util.glu.*;

import src.Message;

/**
 * Collection of functions to init and run an OpenGL app using LWJGL.
 * <P>
 * Includes functions to handle: <BR>
 * Setup display mode, keyboard, mouse, handle events<BR>
 * Run main loop of application <BR>
 * Buffer allocation -- manage IntBuffer, ByteBuffer calls. <BR>
 * OpenGL functions -- convert screen/world coords, set modes, lights, etc. <BR>
 * Utility functions -- load images, convert pixels, getTimeInMillis, etc. <BR>
 * <P>
 * Has a main() function to run as an application, though this class has only
 * minimal placeholder functionality. It is meant to be subclassed, and the
 * subclass will define setup() draw() mouseMove() functions, etc.
 * <P>
 * GLApp initializes the LWJGL environment for OpenGL rendering, ie. creates a
 * window, sets the display mode, inits mouse and keyboard, then runs a loop.
 * <P>
 * Uses GLImage to load and hold image pixels.
 * <P>
 * napier -at- potatoland -dot- org
 * 
 * @see GLImage
 */
public class GLApp
{
	// Just for reference
	public static final String			GLAPP_VERSION			= ".5";

	// Byte size of data types: Used when allocating native buffers
	public static final int				SIZE_DOUBLE				= 8;
	public static final int				SIZE_FLOAT				= 4;
	public static final int				SIZE_INT				= 4;
	public static final int				SIZE_BYTE				= 1;

	// Application settings
	// These can be tweaked in main() before calling app.run()
	// to customize app behavior.
	public static int					finishedKey				= Keyboard.KEY_ESCAPE;			// App will exit when this key is hit (set to 0 for no key exit)
	public static String				window_title			= "OpenGL Window";				// window title, set in initDisplay()
	public static String				configFilename			= "GLApp.cfg";					// init() calls loadSettings() to load initial settings from this file (OPTIONAL)
	public static boolean				hideNativeCursor		= false;						// hide the operating system cursor, see hideNativeCursor()
	public static boolean				disableNativeCursor		= false;						// turn completely off the operating system cursor, see disableNativeCursor()
	public static boolean				VSyncEnabled			= true;						// if true, synchronize screen updates with video refresh rate
	public static boolean				useCurrentDisplay		= false;						// when initing display, use the settings of the desktop (whatever the PC was using before app was started)
	public static boolean				fullScreen				= false;						// full screen or floating window
	public static boolean				showMessages			= true;						// if true, show debug messages, if false show only error messages (see msg() err())
	public static float					aspectRatio				= 0;							// aspect ratio of OpenGL context (if 0, default to displayWidth/displayHeight)

	// Display settings (settings in glapp.cfg will override these)
	// initDisplay() will pick a Display that best matches displayWidth,
	// displayHeight, displayColorBits, displayFrequency.  If these values
	// are -1, initDisplay() will use the desktop screen settings.
	public static int					displayWidth			= -1;
	public static int					displayHeight			= -1;
	public static int					displayColorBits		= -1;
	public static int					displayFrequency		= -1;
	public static int					depthBufferBits			= 24;							// bits per pixel in the depth buffer
	public static int					viewportX, viewportY;									// viewport position (will default to 0,0)
	public static int					viewportW, viewportH;									// viewport size (will default to screen width, height)
	//private static int orthoWidth = 0;
	//private static int orthoHeight = 0;

	// DisplayMode chosen by initDisplay()
	// DM and displayMode are the same thing.
	public static DisplayMode			DM, origDM;											// hold display mode we set, and the display mode when app first executes
	public static DisplayMode			displayMode;											// hold display mode we set (same as DM)

	// Application variables
	// These are set internally but can be read by the
	// subclass application.
	public static Properties			settings				= new Properties();			// holds settings from file GLApp.cfg (see loadSettings())
	public static boolean				finished;												// App will exit when finished is true (when finishedKey is hit: see run())
	public static int					cursorX, cursorY;										// mouse position (see handleEvents())
	public static double				lastFrameTime			= 0;							// ticks since last frame was drawn (see run() and updateTimer())
	public static double				secondsSinceLastFrame	= 0;							// seconds elapsed since last frame was drawn (see updateTimer())
	public static long					ticksPerSecond			= 0;							// used to calc time in millis
	public static int					frameCount				= 0;							// count frames per sec (just to track performance)

	// For copying screen image to a texture
	public static int					screenTextureSize		= 1024;						// how large should texture be to hold screen (see makeTextureForScreen())

	// NIO Buffers to retrieve OpenGL settings.
	// For memory efficiency and performance, instantiate these once, and reuse.
	// see getSetingInt(), getModelviewMatrix(), project(), unProject()
	public static IntBuffer				bufferViewport			= allocInts(16);
	public static FloatBuffer			bufferModelviewMatrix	= allocFloats(16);
	public static FloatBuffer			bufferProjectionMatrix	= allocFloats(16);
	public static FloatBuffer			tmpResult				= allocFloats(16);				// temp var to hold project/unproject results
	public static FloatBuffer			tmpFloats				= allocFloats(4);				// temp var used by setLightPos(), setFog()
	public static ByteBuffer			tmpFloat				= allocBytes(SIZE_FLOAT);		// temp var used by getZDepth()
	public static IntBuffer				tmpInts					= allocInts(16);				// temp var used by getSettingInt()
	public static ByteBuffer			tmpByte					= allocBytes(SIZE_BYTE);		// temp var used by getStencilValue()
	public static ByteBuffer			tmpInt					= allocBytes(GLApp.SIZE_INT);	// temp var used by getPixelColor()

	// Material colors (see setMaterial())
	public static FloatBuffer			mtldiffuse				= allocFloats(4);				// color of the lit surface
	public static FloatBuffer			mtlambient				= allocFloats(4);				// color of the shadowed surface
	public static FloatBuffer			mtlspecular				= allocFloats(4);				// reflection color (typically this is a shade of gray)
	public static FloatBuffer			mtlemissive				= allocFloats(4);				// glow color
	public static FloatBuffer			mtlshininess			= allocFloats(4);				// size of the reflection highlight

	// Misc.
	public static float					rotation				= 0f;							// to rotate cubes (just to put something on screen)
	public static final float			PIOVER180				= 0.0174532925f;				// A constant used in navigation: PI/180
	public static final float			PIUNDER180				= 57.2957795130f;				// A constant used in navigation: 180/PI;
	static Hashtable<String, String>	OpenGLextensions;										// will be populated by extensionExists()
	static double						avgSecsPerFrame			= .01;							// to smooth out motion, keep a moving average of frame render times

	//========================================================================
	// Run main loop of application.  Handle mouse and keyboard input.
	//
	// The functions main(), run() and init() start and run the application.
	// The run() function starts a loop that handles mouse and keyboard events
	// and calls draw() repeatedly.
	//
	//========================================================================

	public static void main(String args[])
	{
		GLApp demo = new GLApp();
		demo.run();
	}

	/**
	 * Runs the application. Calls init() function to setup OpenGL, input and
	 * display. Runs the main loop of the application, which handles mouse and
	 * keyboard input.
	 * <P>
	 * Calls init(), handleEvents(), update() and draw(). <BR>
	 * handleEvents() calls: mouseMove(), mouseDown(), mouseUp(), keyDown(),
	 * keyUp()
	 */
	public void run()
	{
		// hold onto application class in case we need to load images from jar (see getInputStream())
		setRootClass();
		try
		{
			// Init Display, Keyboard, Mouse, OpenGL, load config file
			init();
			// Main loop
			while (!finished)
			{
				if (!Display.isVisible())
				{ // window is minimized
					Thread.sleep(200L);
				}
				else if (Display.isCloseRequested())
				{ // window X button clicked
					finished = true;
				}
				else
				{ // yield a little so other threads can work
					Thread.sleep(1);
				}
				updateTimer(); // track when frame was drawn (see secondsSinceLastFrame)
				handleEvents(); // call key...() and mouse...() functions based on input events
				update(); // do program logic here (subclass may override this)
				draw(); // redraw the screen (subclass overrides this)
				Display.update();
			}
		}
		catch (Exception e)
		{
			Message.err("GLApp.run(): " + e);
			e.printStackTrace(System.out);
		}
		// prepare to exit
		cleanup();
		System.exit(0);
	}

	/**
	 * Called only once when app is first started, init() prepares the display,
	 * mouse and OpenGL context for use. Override init() only if you want to
	 * substantially alter the app startup behavior. Otherwise just override
	 * initGL() to tweak the OpenGL context and setup() to load textures,
	 * models, etc..
	 */
	public void init()
	{
		// load settings from config file (display size, resolution, etc.)
		loadSettings(configFilename);
		initDisplay();
		initInput();
		initGL();
		setup(); // subclass usually overrides this
		updateTimer(); // Do this once to init time values to something sane, otherwise the first game loop will report a huge secondsElapsedPerFrame
	}

	/**
	 * Called by the run() loop. Handles animation and input for each frame.
	 */
	public void handleEvents()
	{
		int mouseDX = Mouse.getDX();
		int mouseDY = Mouse.getDY();
		int mouseDW = Mouse.getDWheel();
		// handle mouse motion
		if (mouseDX != 0 || mouseDY != 0 || mouseDW != 0)
		{
			cursorX += mouseDX;
			cursorY += mouseDY;
			if (cursorX < 0)
			{
				cursorX = 0;
			}
			else if (cursorX > displayMode.getWidth())
			{
				cursorX = displayMode.getWidth();
			}
			if (cursorY < 0)
			{
				cursorY = 0;
			}
			else if (cursorY > displayMode.getHeight())
			{
				cursorY = displayMode.getHeight();
			}
			mouseMove(cursorX, cursorY);
			//msg("DX=" + mouseDX + " DY=" + mouseDY + " cursorX=" + cursorX);
		}
		// handle mouse wheel event
		if (mouseDW != 0)
		{
			mouseWheel(mouseDW);
		}
		// handle mouse clicks
		while (Mouse.next())
		{
			if (Mouse.getEventButton() == 0 && Mouse.getEventButtonState() == true)
			{
				mouseDown(cursorX, cursorY);
			}
			if (Mouse.getEventButton() == 0 && Mouse.getEventButtonState() == false)
			{
				mouseUp(cursorX, cursorY);
			}
			if (Mouse.getEventButton() == 1 && Mouse.getEventButtonState() == true)
			{
				mouseDown(cursorX, cursorY);
			}
			if (Mouse.getEventButton() == 1 && Mouse.getEventButtonState() == false)
			{
				mouseUp(cursorX, cursorY);
			}
		}
		// Handle key hits
		while (Keyboard.next())
		{
			// check for exit key
			if (Keyboard.getEventKey() == finishedKey)
			{
				finished = true;
			}
			// pass key event to handler
			if (Keyboard.getEventKeyState())
			{ // key was just pressed, trigger keyDown()
				keyDown(Keyboard.getEventKey());
			}
			else
			{
				keyUp(Keyboard.getEventKey()); // key was released
			}
		}

		// Count frames
		frameCount++;
		if ((Sys.getTime() - lastFrameTime) > ticksPerSecond)
		{
			//msg("==============> FramesPerSec=" + (frameCount/1) + " timeinsecs=" + getTimeInSeconds() + " timeinmillis=" + getTimeInMillis());
			frameCount = 0;
		}
	}

	/**
	 * Load configuration settings from optional properties file. File format
	 * is:<BR>
	 * 
	 * <PRE>
	 * # Comment
	 * displayWidth=1024
	 * displayHeight=768
	 * </PRE>
	 * 
	 * @param configFilename
	 */
	public void loadSettings(String configFilename)
	{
		if (configFilename == null || configFilename.equals(""))
		{
			return;
		}
		InputStream configFileIn = getInputStream(configFilename);
		settings = new Properties();
		if (configFileIn == null)
		{
			Message.msg("GLApp.loadSettings() warning: config file " + configFilename + " not found, will use default settings.");
			return;
		}
		else
		{
			try
			{
				settings.load(configFileIn);
			}
			catch (Exception e)
			{
				Message.msg("GLApp.loadSettings() warning: " + e);
				return;
			}
		}
		// Debug: show the settings
		settings.list(System.out);
		// Check for available settings
		if (settings.getProperty("displayWidth") != null)
		{
			displayWidth = Integer.parseInt(settings.getProperty("displayWidth"));
		}
		if (settings.getProperty("displayHeight") != null)
		{
			displayHeight = Integer.parseInt(settings.getProperty("displayHeight"));
		}
		if (settings.getProperty("displayColorBits") != null)
		{
			displayColorBits = Integer.parseInt(settings.getProperty("displayColorBits"));
		}
		if (settings.getProperty("displayFrequency") != null)
		{
			displayFrequency = Integer.parseInt(settings.getProperty("displayFrequency"));
		}
		if (settings.getProperty("depthBufferBits") != null)
		{
			depthBufferBits = Integer.parseInt(settings.getProperty("depthBufferBits"));
		}
		if (settings.getProperty("aspectRatio") != null)
		{
			aspectRatio = Float.parseFloat(settings.getProperty("aspectRatio"));
		}
		if (settings.getProperty("fullScreen") != null)
		{
			fullScreen = settings.getProperty("fullScreen").toUpperCase().equals("YES");
		}
		if (settings.getProperty("useCurrentDisplay") != null)
		{
			useCurrentDisplay = settings.getProperty("useCurrentDisplay").toUpperCase().equals("YES");
		}
		if (settings.getProperty("finishedKey") != null)
		{ // key codes are defined in the lwjgl Keyboard class
			finishedKey = Integer.parseInt(settings.getProperty("finishedKey"));
		}
		if (settings.getProperty("window_title") != null)
		{
			window_title = settings.getProperty("window_title");
		}
		if (settings.getProperty("VSyncEnabled") != null)
		{
			VSyncEnabled = settings.getProperty("VSyncEnabled").toUpperCase().equals("YES");
		}
	}

	//========================================================================
	// Setup display mode
	//
	// Initialize Display, Mouse, Keyboard.
	//
	//========================================================================

	/**
	 * Initialize the Display mode, viewport size, and open a Window. By default
	 * the window is fullscreen, the viewport is the same dimensions as the
	 * window.
	 */
	public boolean initDisplay()
	{
		origDM = Display.getDisplayMode(); // current display settings
		Message.msg("GLApp.initDisplay(): Current display mode is " + origDM);
		// for display properties that have not been specified, default to current display value
		if (displayHeight == -1)
			displayHeight = origDM.getHeight();
		if (displayWidth == -1)
			displayWidth = origDM.getWidth();
		if (displayColorBits == -1)
			displayColorBits = origDM.getBitsPerPixel();
		if (displayFrequency == -1)
			displayFrequency = origDM.getFrequency();
		// Set display mode
		try
		{
			if (useCurrentDisplay)
			{
				// use current display settings (ignore properties file)
				DM = origDM;
			}
			else
			{
				// find a display mode that matches the specified settings (or use a sane alternative)
				if ((DM = getDisplayMode(displayWidth, displayHeight, displayColorBits, displayFrequency)) == null)
				{
					if ((DM = getDisplayMode(1024, 768, 32, 60)) == null)
					{
						if ((DM = getDisplayMode(1024, 768, 16, 60)) == null)
						{
							if ((DM = getDisplayMode(origDM.getWidth(), origDM.getHeight(), origDM.getBitsPerPixel(), origDM.getFrequency())) == null)
							{
								Message.err("GLApp.initDisplay(): Can't find a compatible Display Mode!!!");
							}
						}
					}
				}
			}
			Message.msg("GLApp.initDisplay(): Setting display mode to " + DM + " with pixel depth = " + depthBufferBits);
			Display.setDisplayMode(DM);
			displayMode = DM;
			displayWidth = DM.getWidth();
			displayHeight = DM.getHeight();
			displayColorBits = DM.getBitsPerPixel();
			displayFrequency = DM.getFrequency();
		}
		catch (Exception exception)
		{
			System.err.println("GLApp.initDisplay(): Failed to create display: " + exception);
			System.exit(1); //!!!!new
		}
		// Initialize the Window
		try
		{
			Display.create(new PixelFormat(0, depthBufferBits, 8)); // set bits per buffer: alpha, depth, stencil
			Display.setTitle(window_title);
			Display.setFullscreen(fullScreen);
			Display.setVSyncEnabled(VSyncEnabled);
			//msg("GLApp.initDisplay(): Created OpenGL window.");
		}
		catch (Exception exception1)
		{
			System.err.println("GLApp.initDisplay(): Failed to create OpenGL window: " + exception1);
			System.exit(1);
		}
		// Set viewport width/height and Aspect ratio.
		if (aspectRatio == 0f)
		{
			// no aspect ratio was set in GLApp.cfg: default to full screen.
			aspectRatio = (float) DM.getWidth() / (float) DM.getHeight(); // calc aspect ratio of display
		}
		// viewport may not match shape of screen.  Adjust to fit.
		viewportH = DM.getHeight(); // viewport Height
		viewportW = (int) (DM.getHeight() * aspectRatio); // Width
		if (viewportW > DM.getWidth())
		{
			viewportW = DM.getWidth();
			viewportH = (int) (DM.getWidth() * (1 / aspectRatio));
		}
		// center viewport in screen
		viewportX = (int) ((DM.getWidth() - viewportW) / 2);
		viewportY = (int) ((DM.getHeight() - viewportH) / 2);
		return true;
	}

	/**
	 * Retrieve a DisplayMode object with the given params
	 */
	public static DisplayMode getDisplayMode(int w, int h, int colorBits, int freq)
	{
		try
		{
			DisplayMode allDisplayModes[] = Display.getAvailableDisplayModes();
			DisplayMode dm = null;
			for (int j = 0; j < allDisplayModes.length; j++)
			{
				dm = allDisplayModes[j];
				if (dm.getWidth() == w && dm.getHeight() == h && dm.getBitsPerPixel() == colorBits && dm.getFrequency() == freq)
				{
					return dm;
				}
			}
		}
		catch (LWJGLException lwjgle)
		{
			Message.err("GLApp.getDisplayMode() error:" + lwjgle);
		}
		return null;
	}

	/**
	 * Initialize the Keyboard and Mouse.
	 * <P>
	 * Disable or hide the native cursor. Set the initial cursor position. Get
	 * the timer resolution (ticks per second).
	 * 
	 * @see handleEvents()
	 */
	public void initInput()
	{
		try
		{
			// init keyboard
			Keyboard.create();

			// Turn off native cursor?
			if (disableNativeCursor)
			{
				// Mouse.setGrabbed(true) will turn off the native cursor
				disableNativeCursor(true);
				// set initial cursor pos to center screen
				cursorX = (int) DM.getWidth() / 2;
				cursorY = (int) DM.getHeight() / 2;
			}

			// Hide native cursor when inside application window?
			if (hideNativeCursor)
			{
				hideNativeCursor(true);
			}

			// Init hi-res timer (see time functions)
			ticksPerSecond = Sys.getTimerResolution();
		}
		catch (Exception e)
		{
			Message.err("GLApp.initInput(): " + e);
		}
	}

	//========================================================================
	// Custom Application functionality: can be overriden by subclass.
	//
	// Functions to initialize OpenGL, set the viewing mode, render the scene,
	// respond to mouse actions, and initialize the app.  These functions
	// are overridden in the subclass to create custom behavior.
	//
	//========================================================================

	/**
	 * Initialize the OpenGL context. The subclass can override this function to
	 * customize the OpenGL settings. This function is called by init() once
	 * when app starts, but may be called again to restore settings to a default
	 * state, or to initialize a PBuffer object to the exact same state as the
	 * display.
	 */
	public void initGL()
	{
		try
		{
			// Depth test setup
			GL11.glEnable(GL11.GL_DEPTH_TEST); // Enables Depth Testing
			GL11.glDepthFunc(GL11.GL_LEQUAL); // The Type Of Depth Testing To Do

			// Some basic settings
			GL11.glClearColor(0f, 0f, 0f, 1f); // Black Background
			GL11.glEnable(GL11.GL_NORMALIZE); // force normal lengths to 1
			GL11.glEnable(GL11.GL_CULL_FACE); // don't render hidden faces
			GL11.glEnable(GL11.GL_TEXTURE_2D); // use textures
			GL11.glEnable(GL11.GL_BLEND); // enable transparency

			// How to handle transparency: average colors together
			GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);

			// Enable alpha test so the transparent backgrounds in texture images don't draw.
			// This prevents transparent areas from affecting the depth or stencil buffer.
			// alpha func will accept only fragments with alpha greater than 0
			GL11.glEnable(GL11.GL_ALPHA_TEST);
			GL11.glAlphaFunc(GL11.GL_GREATER, 0f);

			// Draw specular highlghts on top of textures
			GL11.glLightModeli(GL12.GL_LIGHT_MODEL_COLOR_CONTROL, GL12.GL_SEPARATE_SPECULAR_COLOR);

			// Perspective quality
			GL11.glHint(GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_NICEST);

			// Set the size and shape of the screen area
			GL11.glViewport(viewportX, viewportY, viewportW, viewportH);

			// setup perspective (see setOrtho() for 2D)
			setPerspective();

			// select model view for subsequent transformations
			GL11.glMatrixMode(GL11.GL_MODELVIEW);
			GL11.glLoadIdentity();
		}
		catch (Exception e)
		{
			Message.err("GLApp.initOpenGL(): " + e);
		}
	}

	/**
	 * Setup can be overridden by the subclass to initialize the application ie
	 * load textures, models, and create data structures used by the app. This
	 * function is called only once, when application starts. It is called after
	 * initDisplay and initOpenGL(), so the OpenGL context is already created.
	 * 
	 * @see init()
	 */
	public void setup()
	{
	}

	/**
	 * Update can be overridden by the subclass
	 * 
	 * @see run()
	 */
	public void update()
	{
	}

	/**
	 * Called by run() to draw one frame. Subclass will override this. This is
	 * an alias function just to be follow Processing and OpenFrameworks
	 * function naming conventions.
	 */
	public void draw()
	{
		render();
	}

	/**
	 * Same as draw(). Subclass can override render() instead of draw(). Same
	 * thing, just a matter of taste.
	 */
	public void render()
	{
		// rotate 90 degrees per second
		rotation += secondsSinceLastFrame * 90f;

		// clear depth buffer and color
		GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);

		// select model view for subsequent transforms
		GL11.glMatrixMode(GL11.GL_MODELVIEW);
		GL11.glLoadIdentity();

		// set viewpoint 10 units from origin, looking at origin
		GLU.gluLookAt(0, 0, 10, 0, 0, 0, 0, 1, 0);

		// rotate, scale and draw cube
		GL11.glPushMatrix();
		{
			GL11.glRotatef(rotation, 0, 1, 0);
			GL11.glColor4f(0f, .5f, 1f, 1f);
			renderCube();
		}
		GL11.glPopMatrix();

		// draw another overlapping cube
		GL11.glPushMatrix();
		{
			GL11.glRotatef(rotation, 1, 1, 1);
			GL11.glColor4f(.7f, .5f, 0f, 1f);
			renderCube();
		}
		GL11.glPopMatrix();
	}

	/**
	 * Run() calls this right before exit. Free up allocated resources (display
	 * lists) and gracefully shut down OpenGL context.
	 */
	public void cleanup()
	{
		destroyFont();
		destroyDisplayLists();
		Keyboard.destroy();
		Display.destroy(); // will call Mouse.destroy()
	}

	/**
	 * Shutdown the application. This will call cleanup() before exiting from
	 * the application.
	 * 
	 * @see cleanup()
	 */
	public void exit()
	{
		finished = true;
	}

	//========================================================================
	// Mouse events, called by handleEvents()
	//========================================================================

	/**
	 * Called by handleEvents() when mouse moves
	 */
	public void mouseMove(int x, int y)
	{
	}

	public void mouseDown(int x, int y)
	{
	}

	public void mouseUp(int x, int y)
	{
	}

	public void mouseWheel(int wheelMoved)
	{
	}

	/**
	 * Return true if the given mousebutton is down. Typically mouse buttons are
	 * 0=left, 1=right. This function can be called inside mouse events such as
	 * mouseDown() and mouseMove() to see which button is activated.
	 * 
	 * @param whichButton
	 *            number of mouse button (0=left button)
	 */
	public boolean mouseButtonDown(int whichButton)
	{
		return Mouse.isButtonDown(whichButton);
	}

	/**
	 * Called when key is pressed. Keycode will be the key ID value as defined
	 * in the LWJGL Keyboard class.
	 * 
	 * @see Keyboard class in the LWJGL documentation
	 * @param keycode
	 */
	public void keyDown(int keycode)
	{
	}

	/**
	 * Called when key is released. Keycode will be the key ID value as defined
	 * in the LWJGL Keyboard class.
	 * 
	 * @see Keyboard class in the LWJGL documentation
	 * @param keycode
	 */
	public void keyUp(int keycode)
	{
	}

	/**
	 * Return the character associatated with the last key event. When called
	 * inside keyDown() or keyUp() this function will return the character
	 * equivalent of the keycode that was passed to keyDown() or keyUp().
	 */
	public char keyChar()
	{
		return Keyboard.getEventCharacter();
	}

	//========================================================================
	// functions to get values from a Properties object.  Properties can be
	// loaded from a text file containing name=value pairs.
	//========================================================================

	/**
	 * Load configuration settings from a properties file. File format is:<BR>
	 * 
	 * <PRE>
	 * # Comment
	 * displayWidth=1024
	 * displayHeight=768
	 * </PRE>
	 * 
	 * @param configFilename
	 */
	public static Properties loadPropertiesFile(String configFilename)
	{
		Properties props = new Properties();
		try
		{
			settings.load(getInputStream(configFilename));
		}
		catch (Exception e)
		{
			Message.msg("GLApp.loadPropertiesFile() warning: " + e);
			return null;
		}
		return props;
	}

	public static String getProperty(Properties props, String propName)
	{
		String s = null;
		if (propName != null && propName.length() > 0)
		{
			s = props.getProperty(propName);
		}
		return s;
	}

	public static int getPropertyInt(Properties props, String propName)
	{
		String s = getProperty(props, propName);
		int v = -1;
		if (s != null)
		{
			v = Integer.parseInt(s);
		}
		return v;
	}

	public static float getPropertyFloat(Properties props, String propName)
	{
		String s = getProperty(props, propName);
		float v = -1f;
		if (s != null)
		{
			v = Float.parseFloat(s);
		}
		return v;
	}

	public static boolean getPropertyBool(Properties props, String propName)
	{
		String s = getProperty(props, propName);
		boolean v = false;
		if (s != null)
		{
			v = (s.toUpperCase().equals("YES") || s.toUpperCase().equals("TRUE") || s.equals("1"));
		}
		return v;
	}

	/**
	 * Return a property from the application configuration file (the filename
	 * given in the configFilename variable). This file is optional, so
	 * properties may be empty.
	 * 
	 * @see loadSettings()
	 */
	public static String getProperty(String propertyName)
	{
		return settings.getProperty(propertyName);
	}

	/**
	 * return the width of the Viewport (the screen area that OpenGL will draw
	 * into). By default the viewport is the same size as the Display (see
	 * getWidthWindow()), however the setViewport() function can set the
	 * viewport to a sub-region of the screen.
	 * <P>
	 * This function is only valid after app is running and Display has been
	 * initialized.
	 * 
	 * @see setViewport(int,int,int,int)
	 */
	public static int getWidth()
	{
		return viewportW;
	}

	/**
	 * return the height of the Viewport (the screen area that OpenGL will draw
	 * into). By default the viewport is the same size as the Display (see
	 * getHeightWindow()), however the setViewport() function can set the
	 * viewport to a sub-region of the screen.
	 * <P>
	 * This function is only valid after app is running and Display has been
	 * initialized.
	 * 
	 * @see setViewport(int,int,int,int)
	 */
	public static int getHeight()
	{
		return viewportH;
	}

	/**
	 * return the Display width (the width of the full window). Only valid after
	 * app is running and Display has been initialized.
	 */
	public static int getWidthWindow()
	{
		return displayWidth;
	}

	/**
	 * return the Display height (the height of the full window). Only valid
	 * after app is running and Display has been initialized.
	 */
	public static int getHeightWindow()
	{
		return displayHeight;
	}

	//========================================================================
	// functions to set basic application behavior
	//========================================================================

	/**
	 * Set the background color of the screen. The red,green,blue color
	 * components are floats in the range 0-1. Black is 0,0,0 and white is
	 * 1,1,1. Color will take effect the next time the screen is cleared.
	 */
	public static void setBackgroundColor(float R, float G, float B)
	{
		GL11.glClearColor(R, G, B, 1);
	}

	/**
	 * If the param is true, turn off the hardware cursor. The application can
	 * decide how to respond to mouse events and whether to draw a position
	 * indicator on screen or not. If running in a window (not fullscreen),
	 * there will be no hardware cursor visible and the user cannot move or
	 * click outside the window area.
	 * <P>
	 * If the param is false, the hardware cursor will behave normally. Use
	 * hideHardwareCursor() to show or hide the hardware cursor.
	 * 
	 * @see hideHardwareCursor()
	 */
	public static void disableNativeCursor(boolean off)
	{
		disableNativeCursor = off;
		Mouse.setGrabbed(off);
	}

	/**
	 * If param is true, make the native cursor transparent. Cursor will be
	 * hidden in the window area, but will be visible outside the window
	 * (assuming you're not in fullscreen mode). I also used this approach with
	 * touch screens because the touch screen drivers needed to read the
	 * hardware mouse position, so I couldn't disable the hardware cursor, but I
	 * wanted to hide it.
	 * <P>
	 * If param is false, reset the cursor to the default.
	 * 
	 * @see disableHardwareCursor()
	 */
	public static void hideNativeCursor(boolean hide)
	{
		hideNativeCursor = hide;
		if ((Cursor.getCapabilities() & Cursor.CURSOR_ONE_BIT_TRANSPARENCY) == 0)
		{
			Message.err("GLApp.hideHardwareCursor(): No hardwared cursor support!");
			return;
		}
		try
		{
			if (hide)
			{
				Cursor cursor = null;
				int cursorImageCount = 1;
				int cursorWidth = Cursor.getMaxCursorSize();
				int cursorHeight = cursorWidth;
				IntBuffer cursorImages;
				IntBuffer cursorDelays = null;
				// Create a single cursor, completely transparent
				cursorImages = ByteBuffer.allocateDirect(cursorWidth * cursorHeight * cursorImageCount * SIZE_INT).order(ByteOrder.nativeOrder()).asIntBuffer();
				for (int j = 0; j < cursorWidth; j++)
				{
					for (int l = 0; l < cursorHeight; l++)
					{
						cursorImages.put(0x00000000);
					}
				}
				cursorImages.flip();
				cursor = new Cursor(Cursor.getMaxCursorSize(), Cursor.getMaxCursorSize(), Cursor.getMaxCursorSize() / 2, Cursor.getMaxCursorSize() / 2, cursorImageCount, cursorImages, cursorDelays);
				// turn it on
				Mouse.setNativeCursor(cursor);
			}
			else
			{
				Mouse.setNativeCursor(null);
			}
		}
		catch (Exception e)
		{
			Message.err("GLApp.hideHardwareCursor(): error " + e);
		}
	}

	/**
	 * Set the cursorX,cursorY position. This will set the screen position of
	 * the native cursor also, unless hideCursor() was called.
	 * 
	 * @param screenX
	 * @param screenY
	 */
	public static void setCursorPosition(int screenX, int screenY)
	{
		Mouse.setCursorPosition(screenX, screenY);
		cursorX = screenX;
		cursorY = screenY;
	}

	//========================================================================
	// Matrix functions: get settings, get matrices, convert
	// screen to world coords.
	//========================================================================

	/**
	 * retrieve a setting from OpenGL (calls glGetInteger())
	 * 
	 * @param whichSetting
	 *            the id number of the value to be returned (same constants as
	 *            for glGetInteger())
	 */
	public static int getSettingInt(int whichSetting)
	{
		tmpInts.clear();
		GL11.glGetInteger(whichSetting, tmpInts);
		return tmpInt.get(0);
	}

	public static FloatBuffer getModelviewMatrix()
	{
		bufferModelviewMatrix.clear();
		GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, bufferModelviewMatrix);
		return bufferModelviewMatrix;
	}

	public static FloatBuffer getProjectionMatrix()
	{
		bufferProjectionMatrix.clear();
		GL11.glGetFloat(GL11.GL_PROJECTION_MATRIX, bufferProjectionMatrix);
		return bufferProjectionMatrix;
	}

	public static IntBuffer getViewport()
	{
		bufferViewport.clear();
		GL11.glGetInteger(GL11.GL_VIEWPORT, bufferViewport);
		return bufferViewport;
	}

	/**
	 * Convert a FloatBuffer matrix to a 4x4 float array.
	 * 
	 * @param fb
	 *            FloatBuffer containing 16 values of 4x4 matrix
	 * @return 2 dimensional float array
	 */
	public static float[][] getMatrixAsArray(FloatBuffer fb)
	{
		float[][] fa = new float[4][4];
		fa[0][0] = fb.get();
		fa[0][1] = fb.get();
		fa[0][2] = fb.get();
		fa[0][3] = fb.get();
		fa[1][0] = fb.get();
		fa[1][1] = fb.get();
		fa[1][2] = fb.get();
		fa[1][3] = fb.get();
		fa[2][0] = fb.get();
		fa[2][1] = fb.get();
		fa[2][2] = fb.get();
		fa[2][3] = fb.get();
		fa[3][0] = fb.get();
		fa[3][1] = fb.get();
		fa[3][2] = fb.get();
		fa[3][3] = fb.get();
		return fa;
	}

	/**
	 * Return the Z depth of the single pixel at the given screen position.
	 */
	public static float getZDepth(int x, int y)
	{
		tmpFloat.clear();
		GL11.glReadPixels(x, y, 1, 1, GL11.GL_DEPTH_COMPONENT, GL11.GL_FLOAT, tmpFloat);
		return ((float) (tmpFloat.getFloat(0)));
	}

	/**
	 * Find the Z depth of the origin in the projected world view. Used by
	 * getWorldCoordsAtScreen() Projection matrix must be active for this to
	 * return correct results (GL.glMatrixMode(GL.GL_PROJECTION)). For some
	 * reason I have to chop this to four decimals or I get bizarre results when
	 * I use the value in project().
	 */
	public static float getZDepthAtOrigin()
	{
		float[] resultf = new float[3];
		project(0, 0, 0, resultf);
		return ((int) (resultf[2] * 10000F)) / 10000f; // truncate to 4 decimals
	}

	/**
	 * Return screen coordinates for a given point in world space. The world
	 * point xyz is 'projected' into screen coordinates using the current model
	 * and projection matrices, and the current viewport settings.
	 * 
	 * @param x
	 *            world coordinates
	 * @param y
	 * @param z
	 * @param resultf
	 *            the screen coordinate as an array of 3 floats
	 */
	public static void project(float x, float y, float z, float[] resultf)
	{
		// lwjgl 2.0 altered params for GLU funcs
		GLU.gluProject(x, y, z, getModelviewMatrix(), getProjectionMatrix(), getViewport(), tmpResult);
		resultf[0] = tmpResult.get(0);
		resultf[1] = tmpResult.get(1);
		resultf[2] = tmpResult.get(2);
	}

	/**
	 * Return world coordinates for a given point on the screen. The screen
	 * point xyz is 'un-projected' back into world coordinates using the current
	 * model and projection matrices, and the current viewport settings.
	 * 
	 * @param x
	 *            screen x position
	 * @param y
	 *            screen y position
	 * @param z
	 *            z-buffer depth position
	 * @param resultf
	 *            the world coordinate as an array of 3 floats
	 * @see getWorldCoordsAtScreen()
	 */
	public static void unProject(float x, float y, float z, float[] resultf)
	{
		GLU.gluUnProject(x, y, z, getModelviewMatrix(), getProjectionMatrix(), getViewport(), tmpResult);
		resultf[0] = tmpResult.get(0);
		resultf[1] = tmpResult.get(1);
		resultf[2] = tmpResult.get(2);
	}

	/**
	 * For given screen xy, return the world xyz coords in a float array. Assume
	 * Z position is 0.
	 */
	public static float[] getWorldCoordsAtScreen(int x, int y)
	{
		float z = getZDepthAtOrigin();
		float[] resultf = new float[3];
		unProject((float) x, (float) y, (float) z, resultf);
		return resultf;
	}

	/**
	 * For given screen xy and z depth, return the world xyz coords in a float
	 * array.
	 */
	public static float[] getWorldCoordsAtScreen(int x, int y, float z)
	{
		float[] resultf = new float[3];
		unProject((float) x, (float) y, (float) z, resultf);
		return resultf;
	}

	//========================================================================
	// Texture functions
	//========================================================================

	/**
	 * Allocate a texture (glGenTextures) and return the handle to it.
	 */
	public static int allocateTexture()
	{
		IntBuffer textureHandle = allocInts(1);
		GL11.glGenTextures(textureHandle);
		return textureHandle.get(0);
	}

	/**
	 * "Select" the given texture for further texture operations.
	 */
	public static void activateTexture(int textureHandle)
	{
		GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureHandle);
	}

	/**
	 * Create a texture and mipmap from the given image file.
	 */
	public static int makeTexture(String textureImagePath)
	{
		int textureHandle = 0;
		GLImage textureImg = loadImage(textureImagePath);
		if (textureImg != null)
		{
			textureHandle = makeTexture(textureImg);
			makeTextureMipMap(textureHandle, textureImg);
		}
		return textureHandle;
	}

	/**
	 * Create a texture and optional mipmap with the given parameters.
	 * 
	 * @param mipmap
	 *            : if true, create mipmaps for the texture
	 * @param anisotropic
	 *            : if true, enable anisotropic filtering
	 */
	public static int makeTexture(String textureImagePath, boolean mipmap, boolean anisotropic)
	{
		int textureHandle = 0;
		GLImage textureImg = loadImage(textureImagePath);
		if (textureImg != null)
		{
			textureHandle = makeTexture(textureImg.pixelBuffer, textureImg.w, textureImg.h, anisotropic);
			if (mipmap)
			{
				makeTextureMipMap(textureHandle, textureImg);
			}
		}
		return textureHandle;
	}

	/**
	 * Create a texture from the given image.
	 */
	public static int makeTexture(GLImage textureImg)
	{
		if (textureImg != null)
		{
			if (isPowerOf2(textureImg.w) && isPowerOf2(textureImg.h))
			{
				return makeTexture(textureImg.pixelBuffer, textureImg.w, textureImg.h, false);
			}
			else
			{
				Message.msg("GLApp.makeTexture(GLImage) Warning: not a power of two: " + textureImg.w + "," + textureImg.h);
				textureImg.convertToPowerOf2();
				return makeTexture(textureImg.pixelBuffer, textureImg.w, textureImg.h, false);
			}
		}
		return 0;
	}

	/**
	 * De-allocate the given texture (glDeleteTextures()).
	 */
	public static void deleteTexture(int textureHandle)
	{
		IntBuffer bufferTxtr = allocInts(1).put(textureHandle);;
		GL11.glDeleteTextures(bufferTxtr);
	}

	/**
	 * Returns true if n is a power of 2. If n is 0 return zero.
	 */
	public static boolean isPowerOf2(int n)
	{
		if (n == 0)
		{
			return false;
		}
		return (n & (n - 1)) == 0;
	}

	/**
	 * Create a blank square texture with the given size.
	 * 
	 * @return the texture handle
	 */
	public static int makeTexture(int w)
	{
		ByteBuffer pixels = allocBytes(w * w * SIZE_INT); // allocate 4 bytes per pixel
		return makeTexture(pixels, w, w, false);
	}

	/**
	 * Create a texture from the given pixels in the default Java ARGB int
	 * format.<BR>
	 * Configure the texture to repeat in both directions and use LINEAR for
	 * magnification.
	 * <P>
	 * 
	 * @return the texture handle
	 */
	public static int makeTexture(int[] pixelsARGB, int w, int h, boolean anisotropic)
	{
		if (pixelsARGB != null)
		{
			ByteBuffer pixelsRGBA = GLImage.convertImagePixelsRGBA(pixelsARGB, w, h, true);
			return makeTexture(pixelsRGBA, w, h, anisotropic);
		}
		return 0;
	}

	/**
	 * Create a texture from the given pixels in the default OpenGL RGBA pixel
	 * format. Configure the texture to repeat in both directions and use LINEAR
	 * for magnification.
	 * <P>
	 * 
	 * @return the texture handle
	 */
	public static int makeTexture(ByteBuffer pixels, int w, int h, boolean anisotropic)
	{
		// get a new empty texture
		int textureHandle = allocateTexture();
		// preserve currently bound texture, so glBindTexture() below won't affect anything)
		GL11.glPushAttrib(GL11.GL_TEXTURE_BIT);
		// 'select' the new texture by it's handle
		GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureHandle);
		// set texture parameters
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR); //GL11.GL_NEAREST);
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR); //GL11.GL_NEAREST);

		// make texture "anisotropic" so it will minify more gracefully
		if (anisotropic && extensionExists("GL_EXT_texture_filter_anisotropic"))
		{
			// Due to LWJGL buffer check, you can't use smaller sized buffers (min_size = 16 for glGetFloat()).
			FloatBuffer max_a = allocFloats(16);
			// Grab the maximum anisotropic filter.
			GL11.glGetFloat(EXTTextureFilterAnisotropic.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, max_a);
			// Set up the anisotropic filter.
			GL11.glTexParameterf(GL11.GL_TEXTURE_2D, EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT, max_a.get(0));
		}

		// Create the texture from pixels
		GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, // level of detail
				GL11.GL_RGBA8, // internal format for texture is RGB with Alpha
				w, h, // size of texture image
				0, // no border
				GL11.GL_RGBA, // incoming pixel format: 4 bytes in RGBA order
				GL11.GL_UNSIGNED_BYTE, // incoming pixel data type: unsigned bytes
				pixels); // incoming pixels

		// restore previous texture settings
		GL11.glPopAttrib();

		return textureHandle;
	}

	/**
	 * Create a texture from the given pixels in ARGB format. The pixels buffer
	 * contains 4 bytes per pixel, in ARGB order. ByteBuffers are created with
	 * native hardware byte orders, so the pixels can be in big-endian (ARGB)
	 * order, or little-endian (BGRA) order. Set the pixel_byte_order
	 * accordingly when creating the texture.
	 * <P>
	 * Configure the texture to repeat in both directions and use LINEAR for
	 * magnification.
	 * <P>
	 * NOTE: I'm having problems creating mipmaps when image pixel data is in
	 * GL_BGRA format. Looks like GLU type param doesn't recognize
	 * GL_UNSIGNED_INT_8_8_8_8 and GL_UNSIGNED_INT_8_8_8_8_REV so I can't
	 * specify endian byte order. Mipmaps look right on PC but colors are
	 * reversed on Mac. Have to stick with GL_RGBA byte order for now.
	 * <P>
	 * 
	 * @return the texture handle
	 */
	public static int makeTextureARGB(ByteBuffer pixels, int w, int h)
	{
		// byte buffer has ARGB ints in little endian or big endian byte order
		int pixel_byte_order = (pixels.order() == ByteOrder.BIG_ENDIAN) ? GL12.GL_UNSIGNED_INT_8_8_8_8 : GL12.GL_UNSIGNED_INT_8_8_8_8_REV;
		// get a new empty texture
		int textureHandle = allocateTexture();
		// 'select' the new texture by it's handle
		GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureHandle);
		// set texture parameters
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR); //GL11.GL_NEAREST);
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR); //GL11.GL_NEAREST);
		// Create the texture from pixels
		GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, // level of detail
				GL11.GL_RGBA8, // internal format for texture is RGB with Alpha
				w, h, // size of texture image
				0, // no border
				GL12.GL_BGRA, // incoming pixel format: 4 bytes in ARGB order
				pixel_byte_order, // incoming pixel data type: little or big endian ints
				pixels); // incoming pixels
		return textureHandle;
	}

	/**
	 * Build Mipmaps for currently bound texture (builds a set of textures at
	 * various levels of detail so that texture will scale up and down
	 * gracefully)
	 * 
	 * @param textureImg
	 *            the texture image
	 * @return error code of buildMipMap call
	 */
	public static int makeTextureMipMap(int textureHandle, GLImage textureImg)
	{
		int ret = 0;
		if (textureImg != null && textureImg.isLoaded())
		{
			GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureHandle);
			ret = GLU.gluBuild2DMipmaps(GL11.GL_TEXTURE_2D, GL11.GL_RGBA8, textureImg.w, textureImg.h, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, textureImg.getPixelBytes());
			if (ret != 0)
			{
				Message.err("GLApp.makeTextureMipMap(): Error occured while building mip map, ret=" + ret + " error=" + GLU.gluErrorString(ret));
			}
			// Assign the mip map levels and texture info
			GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR_MIPMAP_NEAREST);
			GL11.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
		}
		return ret;
	}

	/**
	 * Create a texture large enough to hold the screen image. Use RGBA8 format
	 * to insure colors are copied exactly. Use GL_NEAREST for magnification to
	 * prevent slight blurring of image when screen is drawn back.
	 * 
	 * @see frameCopy()
	 * @see frameDraw()
	 */
	public static int makeTextureForScreen(int screenSize)
	{
		// get a texture size big enough to hold screen (512, 1024, 2048 etc.)
		screenTextureSize = getPowerOfTwoBiggerThan(screenSize);
		Message.msg("GLApp.makeTextureForScreen(): made texture for screen with size " + screenTextureSize);
		// get a new empty texture
		int textureHandle = allocateTexture();
		ByteBuffer pixels = allocBytes(screenTextureSize * screenTextureSize * SIZE_INT);
		// 'select' the new texture by it's handle
		GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureHandle);
		// set texture parameters
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
		// use GL_NEAREST to prevent blurring during frequent screen restores
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
		// Create the texture from pixels: use GL_RGBA8 to insure exact color copy
		GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, screenTextureSize, screenTextureSize, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, pixels);
		return textureHandle;
	}

	/**
	 * Find a power of two equal to or greater than the given value. Ie.
	 * getPowerOfTwoBiggerThan(800) will return 1024.
	 * <P>
	 * 
	 * @see makeTextureForScreen()
	 * @param dimension
	 * @return a power of two equal to or bigger than the given dimension
	 */
	public static int getPowerOfTwoBiggerThan(int n)
	{
		if (n < 0)
			return 0;
		--n;
		n |= n >> 1;
		n |= n >> 2;
		n |= n >> 4;
		n |= n >> 8;
		n |= n >> 16;
		return n + 1;
	}

	/**
	 * Copy pixels from a ByteBuffer to a texture. The buffer pixels are
	 * integers in ARGB format (this is the Java default format you get from a
	 * BufferedImage) or BGRA format (this is the native order of Intel systems.
	 * The glTexSubImage2D() call treats the incoming pixels as integers in
	 * either big-endian (ARGB) or little-endian (BGRA) formats based on the
	 * setting of the bytebuffer (pixel_byte_order).
	 * 
	 * @param bb
	 *            ByteBuffer of pixels stored as ARGB or BGRA integers
	 * @param w
	 *            width of source image
	 * @param h
	 *            height of source image
	 * @param textureHandle
	 *            texture to copy pixels into
	 */
	public static void copyPixelsToTexture(ByteBuffer bb, int w, int h, int textureHandle)
	{
		int pixel_byte_order = (bb.order() == ByteOrder.BIG_ENDIAN) ? GL12.GL_UNSIGNED_INT_8_8_8_8 : GL12.GL_UNSIGNED_INT_8_8_8_8_REV;

		// "select" the texture that we'll write into
		GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureHandle);

		// Copy pixels to texture
		GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, // always GL_TEXTURE_2D
				0, // texture detail level: always 0
				0, 0, // x,y offset into texture
				w, h, // dimensions of image pixel data
				GL12.GL_BGRA, // format of pixels in texture (little_endian - native for PC)
				pixel_byte_order, // format of pixels in bytebuffer (big or little endian ARGB integers)
				bb // image pixel data
		);
	}

	/**
	 * Calls glTexSubImage2D() to copy pixels from an image into a texture.
	 */
	public static void copyImageToTexture(GLImage img, int textureHandle)
	{
		copyPixelsToTexture(img.pixelBuffer, img.w, img.h, textureHandle);
	}

	//========================================================================
	// functions to set projection
	//========================================================================

	/**
	 * Set OpenGL to render in 3D perspective. Set the projection matrix using
	 * GLU.gluPerspective(). The projection matrix controls how the scene is
	 * "projected" onto the screen. Think of it as the lens on a camera, which
	 * defines how wide the field of vision is, how deep the scene is, and how
	 * what the aspect ratio will be.
	 */
	public static void setPerspective()
	{
		// select projection matrix (controls perspective)
		GL11.glMatrixMode(GL11.GL_PROJECTION);
		GL11.glLoadIdentity();
		GLU.gluPerspective(40f, aspectRatio, 1f, 1000f);
		// return to modelview matrix
		GL11.glMatrixMode(GL11.GL_MODELVIEW);
	}

	/**
	 * Set OpenGL to render in flat 2D (no perspective) with a one-to-one
	 * relation between screen pixels and world coordinates, ie. if you draw a
	 * 10x10 quad at 100,100 it will appear as a 10x10 pixel square on screen at
	 * pixel position 100,100.
	 * <P>
	 * <B>ABOUT Ortho and Viewport:</B><br>
	 * Let's say we're drawing in 2D and want to have a cinema proportioned
	 * viewport (16x9), and want to bound our 2D rendering into that area ie.
	 * 
	 * <PRE>
	 *           ___________1024,576
	 *          |           |
	 *          |  Scene    |      Set the bounds on the scene geometry
	 *          |___________|      to the viewport size and shape
	 *       0,0
	 * 
	 *           ___________1024,576
	 *          |           |
	 *          |  Ortho    |      Set the projection to cover the same
	 *          |___________|      area as the scene
	 *       0,0
	 * 
	 *           ___________ 1024,768
	 *          |___________|
	 *          |           |1024,672
	 *          |  Viewport |      Set the viewport to the same shape
	 *      0,96|___________|      as scene and ortho, but centered on
	 *          |___________|      screen.
	 *       0,0
	 * </PRE>
	 */
	public static void setOrtho()
	{
		// select projection matrix (controls view on screen)
		GL11.glMatrixMode(GL11.GL_PROJECTION);
		GL11.glLoadIdentity();
		// set ortho to same size as viewport, positioned at 0,0
		GL11.glOrtho(0, viewportW, // left,right
				0, viewportH, // bottom,top
				-500, 500); // Zfar, Znear
		// return to modelview matrix
		GL11.glMatrixMode(GL11.GL_MODELVIEW);
	}

	public static void setOrtho(int width, int height)
	{
		// select projection matrix (controls view on screen)
		GL11.glMatrixMode(GL11.GL_PROJECTION);
		GL11.glLoadIdentity();
		// set ortho to same size as viewport, positioned at 0,0
		GL11.glOrtho(0, width, // left,right
				0, height, // bottom,top
				-500, 500); // Zfar, Znear
		// return to modelview matrix
		GL11.glMatrixMode(GL11.GL_MODELVIEW);
	}

	/**
	 * Set OpenGL to render in flat 2D (no perspective) on top of current scene.
	 * Preserve current projection and model views, and disable depth testing.
	 * Ortho world size will be same as viewport size, so any ortho drawing
	 * (drawQuad(), drawImageFullscreen(), etc.) will be scaled to fit viewport.
	 * <P>
	 * NOTE: if the viewport is the same size as the window (by default it is),
	 * then setOrtho() will make the world coordinates exactly match the screen
	 * pixel positions. This is convenient for mouse interaction, but be warned:
	 * if you setViewport() to something other than fullscreen, then you need to
	 * use getWorldCoordsAtScreen() to convert screen xy to world xy.
	 * <P>
	 * Once Ortho is on, glTranslate() will take pixel coords as arguments, with
	 * the lower left corner 0,0 and the upper right corner 1024,768 (or
	 * whatever your screen size is). !!!
	 * 
	 * @see setOrthoOff()
	 * @see setViewport(int,int,int,int)
	 */
	public static void setOrthoOn()
	{
		// prepare projection matrix to render in 2D
		GL11.glMatrixMode(GL11.GL_PROJECTION);
		GL11.glPushMatrix(); // preserve perspective view
		GL11.glLoadIdentity(); // clear the perspective matrix
		GL11.glOrtho( // turn on 2D mode
				////viewportX,viewportX+viewportW,    // left, right
				////viewportY,viewportY+viewportH,    // bottom, top    !!!
				0, viewportW, // left, right
				0, viewportH, // bottom, top
				-500, 500); // Zfar, Znear
		// clear the modelview matrix
		GL11.glMatrixMode(GL11.GL_MODELVIEW);
		GL11.glPushMatrix(); // Preserve the Modelview Matrix
		GL11.glLoadIdentity(); // clear the Modelview Matrix
		// disable depth test so further drawing will go over the current scene
		GL11.glDisable(GL11.GL_DEPTH_TEST);
	}

	/**
	 * Set the width and height of the Ortho scene. By default GLApp will use
	 * viewportWidth and viewportHeight for the width and height of ortho mode.
	 * You can override this behavior by setting a specific size with
	 * setOrthoDimensions().
	 * <P>
	 * By default ortho mode (setOrtho(), setOrthoOn()) will set the world width
	 * and height to exactly match the screen pixel width and height (it uses
	 * the viewport size, which is typically the exact same size as the opengl
	 * window). This makes it easy to map screen positions to the world space.
	 * Text positions, images and mouse coordinates map pixel-for-pixel to the
	 * screen.
	 * <P>
	 * The drawback is that the ortho world space will change size on different
	 * resolution screens. In low-res screens the ortho world may be 800x600
	 * while in a hi-res screen it could be 1920x1200, which means that anything
	 * drawn in ortho mode may shift on different screens.
	 * <P>
	 * setOrthoDimensions() assigns a fixed size for ortho scenes, independent
	 * of the viewport size. OpenGL will project the ortho world into the
	 * viewport just as it does with a perspective projection. Things drawn in
	 * ortho will stay in the right place on any resolution screen.
	 * <P>
	 * The drawback with this method is that screen coordinates don't
	 * necessarily map exactly to the ortho world. To translate a screen xy to
	 * the ortho world coordinates, use getWorldCoordsAtScreen(x,y) just as with
	 * perspective worlds.
	 * 
	 * @param width
	 *            width of ortho scene
	 * @param height
	 *            height of ortho scene
	 * @see setOrtho()
	 * @see setOrthoOn()
	 * @see setPerspective()
	 */
	/*
	 * public static void setOrthoDimensions(int width, int height) { orthoWidth
	 * = width; orthoHeight = height; }
	 */

	/**
	 * Turn 2D mode off. Return the projection and model views to their
	 * preserved state that was saved when setOrthoOn() was called, and
	 * re-enable depth testing.
	 * 
	 * @see setOrthoOn()
	 */
	public static void setOrthoOff()
	{
		// restore the original positions and views
		GL11.glMatrixMode(GL11.GL_PROJECTION);
		GL11.glPopMatrix();
		GL11.glMatrixMode(GL11.GL_MODELVIEW);
		GL11.glPopMatrix();
		// turn Depth Testing back on
		GL11.glEnable(GL11.GL_DEPTH_TEST);
	}

	/**
	 * Define the position and size of the screen area in which the OpenGL
	 * context will draw. Position and size of the area are given in exact pixel
	 * sizes. By default the viewport is the same size as the window
	 * (displayWidth,displayHeight).
	 * <P>
	 * NOTE: by default the window size, viewport size and setOrtho() size are
	 * all the same, so in ortho mode screen pixel positions exactly match to
	 * world coordinates. THIS IS NO LONGER TRUE if you setViewport() to some
	 * other size. With a custom viewport you need to use
	 * getWorldCoordsAtScreen() to convert screen xy to world xy.
	 * <P>
	 * 
	 * @param x
	 *            position of the lower left of viewport area, in pixels
	 * @param y
	 * @param width
	 *            size of the viewport area, in pixels
	 * @param height
	 * @see setPerspective()
	 * @see setOrtho()
	 * @see setOrthoDimensions(int,int)
	 */
	public static void setViewport(int x, int y, int width, int height)
	{
		viewportX = x;
		viewportY = y;
		viewportW = width;
		viewportH = height;
		aspectRatio = (float) width / (float) height;
		GL11.glViewport(x, y, width, height);
	}

	/**
	 * Reset the viewport to full screen (displayWidth x displayHeight).
	 * 
	 * @see setViewport(int,int,int,int)
	 */
	public static void resetViewport()
	{
		setViewport(0, 0, displayWidth, displayHeight);
	}

	/**
	 * A simple way to set eye position. Calls gluLookat() to place the
	 * viewpoint <distance> units up the Z axis from the given target position,
	 * looking at the target position. The camera is oriented vertically (Y axis
	 * is up). away.
	 */
	public static void lookAt(float lookatX, float lookatY, float lookatZ, float distance)
	{
		// set viewpoint
		GLU.gluLookAt(lookatX, lookatY, lookatZ + distance, // eye is at the same XY as the target, <distance> units up the Z axis
				lookatX, lookatY, lookatZ, // look at the target position
				0, 1, 0); // the Y axis is up
	}

	//========================================================================
	// Functions to push/pop OpenGL settings
	//========================================================================

	/**
	 * preserve all OpenGL settings that can be preserved. Use this function to
	 * isolate settings changes. Call pushAttrib() before calling glEnable(),
	 * glDisable(), glMatrixMode() etc. After your code executes, call
	 * popAttrib() to return to the previous settings. For better performance,
	 * call pushAttrib() with specific settings flags to preserve only specific
	 * settings.
	 * 
	 * @see popAttrib()
	 */
	public static void pushAttrib()
	{
		GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
	}

	/**
	 * preserve the specified OpenGL setting. Call popAttrib() to return to the
	 * preserved state.
	 * 
	 * @see popAttrib()
	 */
	public static void pushAttrib(int attribute_bits)
	{
		GL11.glPushAttrib(attribute_bits);
	}

	/**
	 * preserve the OpenGL settings that will be affected when we draw in ortho
	 * mode over the scene. For example if we're drawing an interface layer,
	 * buttons, popup menus, cursor, text, etc. we need to turn off lighting,
	 * turn on blending, set color to white and turn off depth test.
	 * <P>
	 * call pushAttribOverlay(), enable settings that you need, when done call
	 * popAttrib()
	 * 
	 * @see popAttrib()
	 */
	public static void pushAttribOrtho()
	{
		GL11.glPushAttrib(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_TEXTURE_BIT | GL11.GL_LIGHTING_BIT | GL11.GL_DEPTH_BUFFER_BIT);
	}

	/**
	 * preserve the OpenGL viewport settings.
	 * 
	 * <pre>
	 *       pushAttribViewport();
	 *           setViewport(0,0,displaymode.getWidth(),displaymode.getHeight());
	 *           ... do some drawing outside of previous viewport area
	 *       popAttrib();
	 * </pre>
	 * 
	 * @see popAttrib()
	 */
	public static void pushAttribViewport()
	{
		GL11.glPushAttrib(GL11.GL_VIEWPORT_BIT);
	}

	/**
	 * return to the OpenGL settings that were preserved by the previous
	 * pushAttrib() call.
	 * 
	 * @see pushAttrib()
	 */
	public static void popAttrib()
	{
		GL11.glPopAttrib();
	}

	//========================================================================
	// Lighting functions
	//========================================================================

	/**
	 * Set the color of a 'positional' light (a light that has a specific
	 * position within the scene). <BR>
	 * Pass in an OpenGL light number (GL11.GL_LIGHT1), the 'Diffuse' and
	 * 'Ambient' colors (direct light and reflected light), and the position.<BR>
	 * 
	 * @param GLLightHandle
	 * @param diffuseLightColor
	 * @param ambientLightColor
	 * @param position
	 */
	public static void setLight(int GLLightHandle, float[] diffuseLightColor, float[] ambientLightColor, float[] specularLightColor, float[] position)
	{
		FloatBuffer ltDiffuse = allocFloats(diffuseLightColor);
		FloatBuffer ltAmbient = allocFloats(ambientLightColor);
		FloatBuffer ltSpecular = allocFloats(specularLightColor);
		FloatBuffer ltPosition = allocFloats(position);
		GL11.glLight(GLLightHandle, GL11.GL_DIFFUSE, ltDiffuse); // color of the direct illumination
		GL11.glLight(GLLightHandle, GL11.GL_SPECULAR, ltSpecular); // color of the highlight
		GL11.glLight(GLLightHandle, GL11.GL_AMBIENT, ltAmbient); // color of the reflected light
		GL11.glLight(GLLightHandle, GL11.GL_POSITION, ltPosition);
		GL11.glEnable(GLLightHandle); // Enable the light (GL_LIGHT1 - 7)
		//GL11.glLightf(GLLightHandle, GL11.GL_QUADRATIC_ATTENUATION, .005F);    // how light beam drops off
	}

	public static void setSpotLight(int GLLightHandle, float[] diffuseLightColor, float[] ambientLightColor, float[] position, float[] direction, float cutoffAngle)
	{
		FloatBuffer ltDirection = allocFloats(direction);
		setLight(GLLightHandle, diffuseLightColor, ambientLightColor, diffuseLightColor, position);
		GL11.glLightf(GLLightHandle, GL11.GL_SPOT_CUTOFF, cutoffAngle); // width of the beam
		GL11.glLight(GLLightHandle, GL11.GL_SPOT_DIRECTION, ltDirection); // which way it points
		GL11.glLightf(GLLightHandle, GL11.GL_CONSTANT_ATTENUATION, 2F); // how light beam drops off
		//GL11.glLightf(GLLightHandle, GL11.GL_LINEAR_ATTENUATION, .5F);    // how light beam drops off
		//GL11.glLightf(GLLightHandle, GL11.GL_QUADRATIC_ATTENUATION, .5F);    // how light beam drops off
	}

	/**
	 * Set the color of the Global Ambient Light. Affects all objects in scene
	 * regardless of their placement.
	 */
	public static void setAmbientLight(float[] ambientLightColor)
	{
		put(tmpFloats, ambientLightColor);
		GL11.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, tmpFloats);
	}

	/**
	 * Set the position of a light to the given xyz. NOTE: Positional light
	 * only, not directional.
	 */
	public static void setLightPosition(int GLLightHandle, float x, float y, float z)
	{
		put(tmpFloats, new float[]{x, y, z, 1});
		GL11.glLight(GLLightHandle, GL11.GL_POSITION, tmpFloats);
	}

	/**
	 * Set the position (or direction) of a light to the given xyz.
	 */
	public static void setLightPosition(int GLLightHandle, float[] position)
	{
		put(tmpFloats, position);
		GL11.glLight(GLLightHandle, GL11.GL_POSITION, tmpFloats);
	}

	/**
	 * enable/disable the given light. The light handle parameter is one of the
	 * predefined OpenGL light handle numbers (GL_LIGHT1, GL_LIGHT2 ...
	 * GL_LIGHT7).
	 */
	public static void setLight(int GLLightHandle, boolean on)
	{
		if (on)
		{
			GL11.glEnable(GLLightHandle);
		}
		else
		{
			GL11.glDisable(GLLightHandle);
		}
	}

	/**
	 * Enable/disable lighting. If parameter value is false, this will turn off
	 * all lights and ambient lighting. NOTE: When lighting is disabled,
	 * material colors are disabled as well. Use glColor() to set color
	 * properties when ligthing is off.
	 */
	public static void setLighting(boolean on)
	{
		if (on)
		{
			GL11.glEnable(GL11.GL_LIGHTING);
		}
		else
		{
			GL11.glDisable(GL11.GL_LIGHTING);
		}
	}

	//========================================================================
	// Material functions
	//========================================================================
	public static final float[]	colorClear	= {0f, 0f, 0f, 0f};	// alpha is 0
	public static final float[]	colorBlack	= {0f, 0f, 0f, 1f};
	public static final float[]	colorWhite	= {1f, 1f, 1f, 1f};
	public static final float[]	colorGray	= {.5f, .5f, .5f, 1f};
	public static final float[]	colorRed	= {1f, 0f, 0f, 1f};
	public static final float[]	colorGreen	= {0f, 1f, 0f, 1f};
	public static final float[]	colorBlue	= {0f, 0f, 1f, 1f};

	/**
	 * A simple way to set the current material properties to approximate a
	 * "real" surface. Provide the surface color (float[4]]) and shininess value
	 * (range 0-1).
	 * <P>
	 * Sets diffuse material color to the surfaceColor and ambient material
	 * color to surfaceColor/2. Based on the shiny value (0-1), sets the
	 * specular property to a color between black (0) and white (1), and sets
	 * the shininess property to a value between 0 and 127.
	 * <P>
	 * Lighting must be enabled for material colors to take effect.
	 * <P>
	 * 
	 * @param surfaceColor
	 *            - must be float[4] {R,G,B,A}
	 * @param reflection
	 *            - a float from 0-1 (0=very matte, 1=very shiny)
	 */
	public static void setMaterial(float[] surfaceColor, float shiny)
	{
		float[] reflect = {shiny, shiny, shiny, 1}; // make a shade of gray
		float[] ambient = {surfaceColor[0] * .5f, surfaceColor[1] * .5f, surfaceColor[2] * .5f, 1}; // darker surface color
		mtldiffuse.put(surfaceColor).flip(); // surface directly lit
		mtlambient.put(ambient).flip(); // surface in shadow
		mtlspecular.put(reflect).flip(); // reflected light
		mtlemissive.put(colorBlack).flip(); // no emissive light
		// size of reflection
		int openglShininess = ((int) (shiny * 127f)); // convert 0-1 to 0-127
		if (openglShininess >= 0 && openglShininess <= 127)
		{
			mtlshininess.put(new float[]{openglShininess, 0, 0, 0}).flip();
		}
		applyMaterial();
	}

	/**
	 * Set the four material colors and calls glMaterial() to change the current
	 * material color in OpenGL. Lighting must be enabled for material colors to
	 * take effect.
	 * 
	 * @param shininess
	 *            : size of reflection (0 is matte, 127 is pinpoint reflection)
	 */
	public static void setMaterial(float[] diffuseColor, float[] ambientColor, float[] specularColor, float[] emissiveColor, float shininess)
	{
		mtldiffuse.put(diffuseColor).flip(); // surface directly lit
		mtlambient.put(ambientColor).flip(); // surface in shadow
		mtlspecular.put(specularColor).flip(); // reflection color
		mtlemissive.put(emissiveColor).flip(); // glow color
		if (shininess >= 0 && shininess <= 127)
		{
			mtlshininess.put(new float[]{shininess, 0, 0, 0}).flip(); // size of reflection 0=broad 127=pinpoint
		}
		applyMaterial();
	}

	/**
	 * Alter the material opacity by setting the diffuse material color alpha
	 * value to the given value
	 * 
	 * @para alpha 0=transparent 1=opaque
	 */
	public static void setMaterialAlpha(float alpha)
	{
		if (alpha < 0)
			alpha = 0;
		if (alpha > 1)
			alpha = 1;
		mtldiffuse.put(3, alpha).flip(); // alpha value of diffuse color
		applyMaterial();
	}

	/**
	 * Call glMaterial() to activate these material properties in the OpenGL
	 * environment. These properties will stay in effect until you change them
	 * or disable lighting.
	 */
	public static void applyMaterial()
	{
		GL11.glMaterial(GL11.GL_FRONT, GL11.GL_DIFFUSE, mtldiffuse);
		GL11.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, mtlambient);
		GL11.glMaterial(GL11.GL_FRONT, GL11.GL_SPECULAR, mtlspecular);
		GL11.glMaterial(GL11.GL_FRONT, GL11.GL_EMISSION, mtlemissive);
		GL11.glMaterial(GL11.GL_FRONT, GL11.GL_SHININESS, mtlshininess);
	}

	//========================================================================
	// Fog
	//========================================================================

	/**
	 * Enable atmospheric fog effect, with the given color and density.
	 * 
	 * <PRE>
	 * setFog(new float[]{.5f, .5f, .5f, 1f}, .3f);
	 * </PRE>
	 * 
	 * @param fogColor
	 *            float[4] specifies the RGB fog color value
	 * @param fogDensity
	 *            float in range 0-1 specifies how opaque the fog will be
	 */
	public static void setFog(float[] fogColor, float fogdensity)
	{
		put(tmpFloats, fogColor);
		// turn fog on
		GL11.glEnable(GL11.GL_FOG);
		// mode: GL_EXP2 is dense fog, GL_EXP is thinner, GL_LINEAR is very thin
		GL11.glFogi(GL11.GL_FOG_MODE, GL11.GL_EXP2);
		// start and end (only apply when fog mode=GL_LINEAR
		//GL11.glFogf(GL11.GL_FOG_START, 100f);
		//GL11.glFogf(GL11.GL_FOG_END, 1000f);
		// color
		GL11.glFog(GL11.GL_FOG_COLOR, tmpFloats);
		// density
		GL11.glFogf(GL11.GL_FOG_DENSITY, fogdensity);
		// quality
		GL11.glHint(GL11.GL_FOG_HINT, GL11.GL_NICEST);
	}

	/**
	 * Enable/disable fog effect. Does not change the fog settings.
	 */
	public static void setFog(boolean on)
	{
		if (on)
		{
			GL11.glEnable(GL11.GL_FOG);
		}
		else
		{
			GL11.glDisable(GL11.GL_FOG);
		}
	}

	//========================================================================
	// Time functions
	//========================================================================

	public static double getTimeInSeconds()
	{
		if (ticksPerSecond == 0)
		{
			ticksPerSecond = Sys.getTimerResolution();
		}
		return (((double) Sys.getTime()) / (double) ticksPerSecond);
	}

	public static double getTimeInMillis()
	{
		if (ticksPerSecond == 0)
		{
			ticksPerSecond = Sys.getTimerResolution();
		}
		return (double) ((((double) Sys.getTime()) / (double) ticksPerSecond) * 1000.0);
	}

	/**
	 * Calculate time since we last called updateTimer(). Updates
	 * secondsSinceLastFrame and sets lastFrameTime to the current
	 * Sys.getTime().
	 * <P>
	 * Called by run() at the beginning of each loop.
	 * 
	 * @see run()
	 * @see getSecondsPerFrame()
	 */
	public static void updateTimer()
	{
		// number of frames to average (about one second)
		double numToAvg = 50;
		// calc time elapsed since we last rendered
		secondsSinceLastFrame = (double) (Sys.getTime() - lastFrameTime) / (double) ticksPerSecond;
		lastFrameTime = Sys.getTime();
		// keep a moving average of frame elapsed times
		if (secondsSinceLastFrame < 1)
		{
			avgSecsPerFrame = (double) ((avgSecsPerFrame * numToAvg) + secondsSinceLastFrame) / (numToAvg + 1D);
		}
	}

	/**
	 * Return the moving average of the seconds per frame for the last 50
	 * frames. Useful when animating in real time. Will provide smoother time
	 * deltas than the secondsSinceLastFrame variable, which holds the exact
	 * time elapsed during the last frame (but may jump or lag as processor load
	 * varies).
	 * 
	 * @see updateTimer()
	 */
	public static double getSecondsPerFrame()
	{
		return avgSecsPerFrame;
	}

	/**
	 * Return the moving average of the frames per second for the last 50
	 * frames.
	 * 
	 * @see updateTimer()
	 */
	public static double getFramesPerSecond()
	{
		return 1d / avgSecsPerFrame;
	}

	//========================================================================
	// Load images
	//========================================================================

	/**
	 * Make a blank image of the given size.
	 * 
	 * @return the new GLImage
	 */
	public static GLImage makeImage(int w, int h)
	{
		ByteBuffer pixels = allocBytes(w * h * SIZE_INT);
		return new GLImage(pixels, w, h);
	}

	/**
	 * Load an image from the given file and return a GLImage object.
	 * 
	 * @param image
	 *            filename
	 * @return the loaded GLImage
	 */
	public static GLImage loadImage(String imgFilename)
	{
		GLImage img = new GLImage(imgFilename);
		if (img.isLoaded())
		{
			return img;
		}
		return null;
	}

	/**
	 * Load an image from the given file and return a ByteBuffer containing ARGB
	 * pixels.<BR>
	 * Can be used to create textures. <BR>
	 * 
	 * @param imgFilename
	 * @return
	 */
	public static ByteBuffer loadImagePixels(String imgFilename)
	{
		GLImage img = new GLImage(imgFilename);
		return img.pixelBuffer;
	}

	/**
	 * Draw a cursor image textured onto a quad at cursorX,cursorY. The cursor
	 * image must be loaded into a 32x32 texture. This function can be called
	 * after scene is drawn to place a cursor on top of scene.
	 * <P>
	 * NOTE: the cursor is drawn in screen space, at an absolute screen pixel
	 * location without regard for viewport (temporarily zets viewport to entire
	 * screen).
	 * <P>
	 * See handleEvents() for cursorX cursorY and mouse motion handling.
	 * <P>
	 * Example:
	 * 
	 * <PRE>
	 *    int cursorTxtr;
	 * 
	 *    public void setup() {
	 *        cursorTxtr = makeTexture("images/cursorCrosshair32.gif"); // image must be 32x32
	 *    }
	 * 
	 *    public void draw() {
	 *        // render scene
	 *        ...
	 *        drawCursor(cursorTxtr);
	 *    }
	 * </PRE>
	 * 
	 * @param cursorTextureHandle
	 *            handle to texture containing 32x32 cursor image
	 */
	public static void drawCursor(int cursorTextureHandle)
	{
		// set projection matrix to 2D fullscreen
		GL11.glMatrixMode(GL11.GL_PROJECTION);
		GL11.glPushMatrix(); // preserve perspective view
		GL11.glLoadIdentity(); // clear the perspective matrix
		GL11.glOrtho( // set ortho to exactly match screen size
				0, displayWidth, // left, right
				0, displayHeight, // bottom, top
				-1, 1); // Zfar, Znear
		// clear the modelview matrix
		GL11.glMatrixMode(GL11.GL_MODELVIEW);
		GL11.glPushMatrix(); // preserve current modelview matrix
		GL11.glLoadIdentity(); // clear the modelview matrix

		// preserve current settings then draw cursor
		GL11.glPushAttrib(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_TEXTURE_BIT | GL11.GL_LIGHTING_BIT | GL11.GL_VIEWPORT_BIT);
		{
			// set viewport to full screen
			GL11.glViewport(0, 0, displayWidth, displayHeight);
			// tweak settings
			GL11.glEnable(GL11.GL_TEXTURE_2D); // be sure textures are on
			GL11.glColor4f(1, 1, 1, 1); // no color
			GL11.glDisable(GL11.GL_LIGHTING); // no lighting
			GL11.glDisable(GL11.GL_DEPTH_TEST); // no depth test
			GL11.glEnable(GL11.GL_BLEND); // enable transparency
			GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
			// draw 32x32 cursor image
			drawQuadZ(cursorTextureHandle, cursorX - 15, cursorY - 15, 0, 32, 32);
		}
		GL11.glPopAttrib();

		// restore the previous matrix settings
		GL11.glMatrixMode(GL11.GL_PROJECTION);
		GL11.glPopMatrix();
		GL11.glMatrixMode(GL11.GL_MODELVIEW);
		GL11.glPopMatrix();
	}

	/**
	 * OLD function: this drew the cursor only into the current viewport, and
	 * could not handle difffent ortho coordinate systems (ortho had to be
	 * exactly mapped to screen size). Draw a cursor image textured onto a quad
	 * at cursor position. The cursor image must be loaded into a texture, then
	 * this function can be called after scene is drawn. Uses glPushAttrib() to
	 * preserve the current drawing state. glPushAttrib() may slow performance
	 * down, so in your app you may want to set the states yourself before
	 * calling drawCursor() and take the push/pop out of here.
	 * <P>
	 * See handleEvents() for cursorX cursorY and mouse motion handling.
	 * <P>
	 * Example:
	 * 
	 * <PRE>
	 *    int cursorTxtr;
	 * 
	 *    public void setup() {
	 *        cursorTxtr = makeTexture("images/cursorCrosshair32.gif"); // image must be 32x32
	 *    }
	 * 
	 *    public void draw() {
	 *        // render scene
	 *        ...
	 *        drawCursor(cursorTxtr);
	 *    }
	 * </PRE>
	 * 
	 * @param cursorTextureHandle
	 *            handle to texture containing 32x32 cursor image
	 */
	public static void drawCursorOLD(int cursorTextureHandle)
	{
		setOrthoOn();
		GL11.glPushAttrib(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_TEXTURE_BIT | GL11.GL_LIGHTING_BIT);
		{
			// tweak settings
			GL11.glEnable(GL11.GL_TEXTURE_2D); // be sure textures are on
			GL11.glColor4f(1, 1, 1, 1); // no color
			GL11.glDisable(GL11.GL_LIGHTING); // no lighting
			GL11.glDisable(GL11.GL_DEPTH_TEST); // no depth test
			GL11.glEnable(GL11.GL_BLEND); // enable transparency
			GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
			drawQuad(cursorTextureHandle, cursorX - 15, cursorY - 15, 32, 32); // assumes 32x32 pixels
		}
		GL11.glPopAttrib();
		setOrthoOff();
	}

	/**
	 * Draw an image in ortho mode (2D) over the entire viewport area. Converts
	 * the image to a texture and maps onto a viewport-sized quad. Depth test is
	 * turned off, lighting is off, color is set to white. Alpha blending is on,
	 * so transparent areas will be respected.
	 * <P>
	 * NOTE: By default the viewport is the same size as the window so this
	 * function will draw the image over the entire window. If you setViewport()
	 * to a custom size the image will be drawn into the custom viewport area.
	 * To insure that the image is drawn truly full screen, call resetViewport()
	 * before drawImageFullScreen().
	 * <P>
	 * 
	 * @see loadImage(String)
	 * @see setViewport(int,int,int,int)
	 * @see resetViewport()
	 */
	public static void drawImageFullScreen(GLImage img)
	{
		if (img == null || img.isLoaded() == false)
		{
			return;
		}
		// if image has no texture, convert the image to a texture
		if (img.textureHandle <= 0)
		{
			img.textureHandle = makeTexture(img);
		}
		// Calculate the UV dimensions of the image in the texture
		float maxU = (float) img.w / (float) img.textureW;
		float maxV = (float) img.h / (float) img.textureH;
		// preserve settings
		pushAttribOrtho();
		// switch to 2D projection
		setOrthoOn();
		// tweak settings
		GL11.glEnable(GL11.GL_TEXTURE_2D); // be sure textures are on
		GL11.glColor4f(1, 1, 1, 1); // no color
		GL11.glDisable(GL11.GL_LIGHTING); // no lighting
		GL11.glDisable(GL11.GL_DEPTH_TEST); // no depth test
		GL11.glEnable(GL11.GL_BLEND); // enable transparency
		GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
		// activate the image texture
		GL11.glBindTexture(GL11.GL_TEXTURE_2D, img.textureHandle);
		// draw a textured quad
		GL11.glBegin(GL11.GL_QUADS);
		{
			GL11.glTexCoord2f(0, 0);
			GL11.glVertex3f(0f, 0f, 0f); // Bottom Left

			GL11.glTexCoord2f(maxU, 0);
			GL11.glVertex3f(getWidth(), 0f, 0f); // Bottom Right

			GL11.glTexCoord2f(maxU, maxV);
			GL11.glVertex3f(getWidth(), getHeight(), 0f); // Top Right

			GL11.glTexCoord2f(0, maxV);
			GL11.glVertex3f(0f, getHeight(), 0f); // Top left
		}
		GL11.glEnd();
		// return to previous projection mode
		setOrthoOff();
		// return to previous settings
		popAttrib();
	}

	/**
	 * Draw an image in whichever projection mode is current (does not switch to
	 * ortho mode). Convert the image to a texture and draw onto quad. Will draw
	 * with current settings (light, material, depth, blend, etc.) <BR>
	 * 
	 * @see loadImage()
	 * @see drawQuad()
	 * @see drawImageFullScreen()
	 */
	public static void drawImage(GLImage img, int x, int y, float w, float h)
	{
		// if image has no texture, convert the image to a texture
		if (img.textureHandle <= 0)
		{
			img.textureHandle = makeTexture(img);
		}
		// preserve settings
		pushAttribOrtho();
		// set color to white
		//GL11.glColor4f(1,1,1,1);   // don't force color to white (may want to tint image)
		// activate the image texture
		GL11.glBindTexture(GL11.GL_TEXTURE_2D, img.textureHandle);
		// draw a textured quad
		GL11.glNormal3f(0.0f, 0.0f, 1.0f); // normal faces positive Z
		GL11.glBegin(GL11.GL_QUADS);
		{
			GL11.glTexCoord2f(0f, 0f);
			GL11.glVertex3f((float) x, (float) y, (float) 0);
			GL11.glTexCoord2f(1f, 0f);
			GL11.glVertex3f((float) x + w, (float) y, (float) 0);
			GL11.glTexCoord2f(1f, 1f);
			GL11.glVertex3f((float) x + w, (float) y + h, (float) 0);
			GL11.glTexCoord2f(0f, 1f);
			GL11.glVertex3f((float) x, (float) y + h, (float) 0);
		}
		GL11.glEnd();
		// return to previous settings
		popAttrib();
	}

	/**
	 * Draw a textured quad in Ortho mode (2D) at the given xy, scaled to the
	 * given width and height. Depth test is turned off so quad will be drawn on
	 * top of the current scene. Quad will be drawn with current light and
	 * material if any are active. <BR>
	 * 
	 * @ee loadImage()
	 */
	public static void drawQuad(int textureHandle, int x, int y, float w, float h)
	{
		// activate the specified texture
		GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureHandle);
		// prepare to render in 2D
		setOrthoOn();
		// draw the textured quad
		GL11.glNormal3f(0.0f, 0.0f, 1.0f); // normal faces positive Z
		GL11.glBegin(GL11.GL_QUADS);
		{
			GL11.glTexCoord2f(0f, 0f);
			GL11.glVertex3f((float) x, (float) y, (float) 0);
			GL11.glTexCoord2f(1f, 0f);
			GL11.glVertex3f((float) x + w, (float) y, (float) 0);
			GL11.glTexCoord2f(1f, 1f);
			GL11.glVertex3f((float) x + w, (float) y + h, (float) 0);
			GL11.glTexCoord2f(0f, 1f);
			GL11.glVertex3f((float) x, (float) y + h, (float) 0);
		}
		GL11.glEnd();
		// restore the previous perspective and model views
		setOrthoOff();
	}

	/**
	 * Draw a textured quad at the given xyz position in 3D space. Quad will be
	 * drawn with current settings (ie. light, material, depth test, projection,
	 * etc.)
	 */
	public static void drawQuadZ(int textureHandle, float x, float y, float z, float w, float h)
	{
		GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureHandle);
		// draw  textured quad
		GL11.glNormal3f(0.0f, 0.0f, 1.0f); // normal faces positive Z
		GL11.glBegin(GL11.GL_QUADS);
		{
			GL11.glTexCoord2f(0f, 0f);
			GL11.glVertex3f(x, y, z);
			GL11.glTexCoord2f(1f, 0f);
			GL11.glVertex3f(x + w, y, z);
			GL11.glTexCoord2f(1f, 1f);
			GL11.glVertex3f(x + w, y + h, z);
			GL11.glTexCoord2f(0f, 1f);
			GL11.glVertex3f(x, y + h, z);
		}
		GL11.glEnd();
	}

	//========================================================================
	// Functions to get and set framebuffer pixels
	//========================================================================

	/**
	 * Return a ByteBuffer containing ARGB pixels of the entire screen area.
	 */
	public static ByteBuffer framePixels()
	{
		return framePixels(0, 0, displayMode.getWidth(), displayMode.getHeight());
	}

	/**
	 * Return a ByteBuffer containing ARGB pixels from the given screen area.
	 */
	public static ByteBuffer framePixels(int x, int y, int w, int h)
	{
		// allocate 4 bytes per pixel
		ByteBuffer pixels = allocBytes(w * h * 4);
		// Get pixels from frame buffer in ARGB format.
		GL11.glReadPixels(x, y, w, h, GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, pixels);
		return pixels;
	}

	/**
	 * Return an int array containing ARGB pixels from the given screen area.
	 */
	public static int[] framePixelsInt(int x, int y, int w, int h)
	{
		int[] pixels = new int[w * h];
		ByteBuffer pixelsBB = framePixels(x, y, w, h);
		get(pixelsBB, pixels);
		return pixels;
	}

	/**
	 * Return the color buffer RGB value at the given screen position as
	 * byte[3].
	 * 
	 * @param x
	 *            screen position
	 * @param y
	 * @return rgb byte array
	 */
	public static byte[] getPixelColor(int x, int y)
	{
		// color value will be stored in an integer
		tmpInt.clear();
		// read the framebuffer color value at the given position, as bytes
		GL11.glReadPixels(x, y, 1, 1, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, tmpInt);
		byte[] rgb = new byte[]{tmpInt.get(0), tmpInt.get(1), tmpInt.get(2)};
		return rgb;
	}

	/**
	 * Return the depth buffer value at the given screen position.
	 * 
	 * @param x
	 *            screen position
	 * @param y
	 * @return float Z depth value
	 */
	public static float getPixelDepth(int x, int y)
	{
		return getZDepth(x, y);
	}

	/**
	 * Return the stencil buffer value at the given screen position. Stencil
	 * values are typically bytes (0-255). The value will be returned as an
	 * integer.
	 * 
	 * @param x
	 *            screen position
	 * @param y
	 * @return int stencil value
	 */
	public static int getPixelStencil(int x, int y)
	{
		return getMaskValue(x, y);
	}

	/**
	 * Save entire screen image to a texture. Will copy entire screen even if a
	 * viewport is in use. Texture param must be large enough to hold screen
	 * image (see makeTextureForScreen()).
	 * 
	 * @param txtrHandle
	 *            texture where screen image will be stored
	 * @see frameDraw()
	 * @see makeTextureForScreen()
	 */
	public static void frameCopy(int txtrHandle)
	{
		frameCopy(txtrHandle, 0, 0, DM.getWidth(), DM.getHeight()); // entire screen
	}

	/**
	 * Save a region of the screen to a texture. Texture must be large enough to
	 * hold screen image.
	 * 
	 * @param txtrHandle
	 *            texture where screen region will be stored
	 * @see frameDraw()
	 * @see makeTextureForScreen()
	 */
	public static void frameCopy(int txtrHandle, int x, int y, int w, int h)
	{
		GL11.glColor4f(1, 1, 1, 1); // turn off alpha and color tints
		GL11.glReadBuffer(GL11.GL_BACK);
		GL11.glBindTexture(GL11.GL_TEXTURE_2D, txtrHandle);
		// Copy screen to texture
		GL11.glCopyTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0, x, y, w, h);
	}

	/**
	 * Draw the screen-sized image over entire screen area. The screen image is
	 * stored in the given texture at 0,0 (see frameCopy()) and has the same
	 * dimensions as the current display mode (DM.getWidth(), DM.getHeight()).
	 * <P>
	 * Reset the viewport and ortho mode to full screen (viewport may be
	 * different proportion than screen if custom aspectRatio is set). Draw the
	 * quad the same size as texture so no stretching or compression of image.
	 * 
	 * @param txtrHandle
	 */
	public static void frameDraw(int txtrHandle)
	{
		// keep it opaque
		GL11.glDisable(GL11.GL_BLEND);
		// set viewport to full screen
		GL11.glViewport(0, 0, DM.getWidth(), DM.getHeight());
		// draw square quad that covers entire screen
		drawQuad(txtrHandle, 0, 0, screenTextureSize, screenTextureSize); // draw the full screen image
		// restore viewport to custom aspect ratio
		GL11.glViewport(viewportX, viewportY, viewportW, viewportH);
	}

	/**
	 * Save the current frame buffer to a PNG image. Exactly the same as
	 * screenShot().
	 * 
	 * @see screenShot()
	 */
	public static void frameSave()
	{
		screenShot();
	}

	//========================================================================
	// Functions to render shapes.
	//========================================================================

	/**
	 * Draw a rectangle outline in ortho mode (draws in 2D over the scene). <BR>
	 * 
	 * @see setLineWidth()
	 * @see drawRectZ()
	 */
	public static void drawRect(int x, int y, float w, float h)
	{
		// switch projection to 2D mode
		setOrthoOn();
		// draw rectangle at Z=0
		drawRectZ(x, y, 0, w, h);
		// restore the previous perspective and model views
		setOrthoOff();
	}

	/**
	 * Draw a rectangle outline in world space. Uses opengl line_strip to make
	 * the rectangle. <BR>
	 * 
	 * @see setLineWidth()
	 * @see drawRect()
	 */
	public static void drawRectZ(int x, int y, int z, float w, float h)
	{
		// preserve current settings
		GL11.glPushAttrib(GL11.GL_TEXTURE_BIT | GL11.GL_LIGHTING_BIT);
		// de-activate texture and light
		GL11.glDisable(GL11.GL_TEXTURE_2D);
		GL11.glDisable(GL11.GL_LIGHTING);
		// draw the rectangle
		GL11.glBegin(GL11.GL_LINE_STRIP);
		{
			GL11.glVertex3f((float) x, (float) y, (float) z);
			GL11.glVertex3f((float) x + w, (float) y, (float) z);
			GL11.glVertex3f((float) x + w, (float) y + h, (float) z);
			GL11.glVertex3f((float) x, (float) y + h, (float) z);
			GL11.glVertex3f((float) x, (float) y, (float) z);
		}
		GL11.glEnd();
		// draw points at the corners
		GL11.glBegin(GL11.GL_POINTS);
		{
			GL11.glVertex3f((float) x, (float) y, (float) z);
			GL11.glVertex3f((float) x + w, (float) y, (float) z);
			GL11.glVertex3f((float) x + w, (float) y + h, (float) z);
			GL11.glVertex3f((float) x, (float) y + h, (float) z);
		}
		GL11.glEnd();
		// re-enable settings
		popAttrib();
	}

	/**
	 * Draws a circle with the given radius centered at the given world
	 * position.
	 */
	public static void drawCircle(int x, int y, int radius, int linewidth)
	{
		// switch projection to 2D mode
		setOrthoOn();
		// draw circle at x,y with z=0
		GL11.glPushMatrix();
		{
			GL11.glTranslatef(x, y, 0);
			drawCircle(radius - linewidth, radius, 180);
		}
		GL11.glPopMatrix();
		// restore the previous perspective and model views
		setOrthoOff();
	}

	/**
	 * Draws a circle with the given radius centered at the given world
	 * position.
	 */
	public static void drawCircleZ(int x, int y, int z, int radius, int linewidth)
	{
		GL11.glPushMatrix();
		{
			GL11.glTranslatef(x, y, z);
			drawCircle(radius - linewidth, radius, 180);
		}
		GL11.glPopMatrix();
	}

	/**
	 * Draws a circle centered at 0,0,0. Use translate() to place circle at
	 * desired coords. Inner and outer radius specify width, stepsize is number
	 * of degrees for each segment.
	 */
	public static void drawCircle(float innerRadius, float outerRadius, int numSegments)
	{
		int s = 0; // start
		int e = 360; // end
		int stepSize = 360 / numSegments; // degrees per segment
		GL11.glBegin(GL11.GL_QUAD_STRIP);
		{
			// add first 2 vertices
			float ts = (float) Math.sin(Math.toRadians(s));
			float tc = (float) Math.cos(Math.toRadians(s));
			GL11.glVertex2f(tc * innerRadius, ts * innerRadius);
			GL11.glVertex2f(tc * outerRadius, ts * outerRadius);
			// add intermediate vertices, snap to {step} degrees
			while ((s = ((s + stepSize) / stepSize) * stepSize) < e)
			{
				ts = (float) Math.sin(Math.toRadians(s));
				tc = (float) Math.cos(Math.toRadians(s));
				GL11.glVertex2f(tc * innerRadius, ts * innerRadius);
				GL11.glVertex2f(tc * outerRadius, ts * outerRadius);
			}
			// add last 2 vertices at end angle
			ts = (float) Math.sin(Math.toRadians(e));
			tc = (float) Math.cos(Math.toRadians(e));
			GL11.glVertex2f(tc * innerRadius, ts * innerRadius);
			GL11.glVertex2f(tc * outerRadius, ts * outerRadius);
		}
		GL11.glEnd();
	}

	/**
	 * Render a 2 unit cube centered at origin. Includes texture coordinates and
	 * normals.
	 */
	public static void renderCube()
	{
		GL11.glBegin(GL11.GL_QUADS);
		// Front Face
		GL11.glNormal3f(0.0f, 0.0f, 1.0f);
		GL11.glTexCoord2f(0.0f, 0.0f);
		GL11.glVertex3f(-1.0f, -1.0f, 1.0f); // Bottom Left
		GL11.glTexCoord2f(1.0f, 0.0f);
		GL11.glVertex3f(1.0f, -1.0f, 1.0f); // Bottom Right
		GL11.glTexCoord2f(1.0f, 1.0f);
		GL11.glVertex3f(1.0f, 1.0f, 1.0f); // Top Right
		GL11.glTexCoord2f(0.0f, 1.0f);
		GL11.glVertex3f(-1.0f, 1.0f, 1.0f); // Top Left
		// Back Face
		GL11.glNormal3f(0.0f, 0.0f, -1.0f);
		GL11.glTexCoord2f(1.0f, 0.0f);
		GL11.glVertex3f(-1.0f, -1.0f, -1.0f); // Bottom Right
		GL11.glTexCoord2f(1.0f, 1.0f);
		GL11.glVertex3f(-1.0f, 1.0f, -1.0f); // Top Right
		GL11.glTexCoord2f(0.0f, 1.0f);
		GL11.glVertex3f(1.0f, 1.0f, -1.0f); // Top Left
		GL11.glTexCoord2f(0.0f, 0.0f);
		GL11.glVertex3f(1.0f, -1.0f, -1.0f); // Bottom Left
		// Top Face
		GL11.glNormal3f(0.0f, 1.0f, 0.0f);
		GL11.glTexCoord2f(0.0f, 1.0f);
		GL11.glVertex3f(-1.0f, 1.0f, -1.0f); // Top Left
		GL11.glTexCoord2f(0.0f, 0.0f);
		GL11.glVertex3f(-1.0f, 1.0f, 1.0f); // Bottom Left
		GL11.glTexCoord2f(1.0f, 0.0f);
		GL11.glVertex3f(1.0f, 1.0f, 1.0f); // Bottom Right
		GL11.glTexCoord2f(1.0f, 1.0f);
		GL11.glVertex3f(1.0f, 1.0f, -1.0f); // Top Right
		// Bottom Face
		GL11.glNormal3f(0.0f, -1.0f, 0.0f);
		GL11.glTexCoord2f(1.0f, 1.0f);
		GL11.glVertex3f(-1.0f, -1.0f, -1.0f); // Top Right
		GL11.glTexCoord2f(0.0f, 1.0f);
		GL11.glVertex3f(1.0f, -1.0f, -1.0f); // Top Left
		GL11.glTexCoord2f(0.0f, 0.0f);
		GL11.glVertex3f(1.0f, -1.0f, 1.0f); // Bottom Left
		GL11.glTexCoord2f(1.0f, 0.0f);
		GL11.glVertex3f(-1.0f, -1.0f, 1.0f); // Bottom Right
		// Right face
		GL11.glNormal3f(1.0f, 0.0f, 0.0f);
		GL11.glTexCoord2f(1.0f, 0.0f);
		GL11.glVertex3f(1.0f, -1.0f, -1.0f); // Bottom Right
		GL11.glTexCoord2f(1.0f, 1.0f);
		GL11.glVertex3f(1.0f, 1.0f, -1.0f); // Top Right
		GL11.glTexCoord2f(0.0f, 1.0f);
		GL11.glVertex3f(1.0f, 1.0f, 1.0f); // Top Left
		GL11.glTexCoord2f(0.0f, 0.0f);
		GL11.glVertex3f(1.0f, -1.0f, 1.0f); // Bottom Left
		// Left Face
		GL11.glNormal3f(-1.0f, 0.0f, 0.0f);
		GL11.glTexCoord2f(0.0f, 0.0f);
		GL11.glVertex3f(-1.0f, -1.0f, -1.0f); // Bottom Left
		GL11.glTexCoord2f(1.0f, 0.0f);
		GL11.glVertex3f(-1.0f, -1.0f, 1.0f); // Bottom Right
		GL11.glTexCoord2f(1.0f, 1.0f);
		GL11.glVertex3f(-1.0f, 1.0f, 1.0f); // Top Right
		GL11.glTexCoord2f(0.0f, 1.0f);
		GL11.glVertex3f(-1.0f, 1.0f, -1.0f); // Top Left
		GL11.glEnd();
	}

	/**
	 * draw a cube with the given size, centered at origin. Include texture
	 * coordinates.
	 * 
	 * @param size
	 *            length of each side
	 * @param segments
	 *            # segments to divide each side into
	 */
	public static void renderCube(float size, int segments)
	{
		float halfsize = size / 2f;
		GL11.glPushMatrix();
		{
			GL11.glPushMatrix();
			{
				GL11.glTranslatef(0, 0, halfsize);
				renderPlane(size, segments);// front
			}
			GL11.glPopMatrix();
			GL11.glPushMatrix();
			{
				GL11.glRotatef(90, 0, 1, 0);
				GL11.glTranslatef(0, 0, halfsize);
				renderPlane(size, segments);// right
			}
			GL11.glPopMatrix();
			GL11.glPushMatrix();
			{
				GL11.glRotatef(180, 0, 1, 0);
				GL11.glTranslatef(0, 0, halfsize);
				renderPlane(size, segments);// back
			}
			GL11.glPopMatrix();
			GL11.glPushMatrix();
			{
				GL11.glRotatef(270, 0, 1, 0);
				GL11.glTranslatef(0, 0, halfsize);
				renderPlane(size, segments);// left
			}
			GL11.glPopMatrix();
			GL11.glPushMatrix();
			{
				GL11.glRotatef(90, 1, 0, 0);
				GL11.glTranslatef(0, 0, halfsize);
				renderPlane(size, segments);// bottom
			}
			GL11.glPopMatrix();
			GL11.glPushMatrix();
			{
				GL11.glRotatef(-90, 1, 0, 0);
				GL11.glTranslatef(0, 0, halfsize);
				renderPlane(size, segments);// top
			}
			GL11.glPopMatrix();
		}
		GL11.glPopMatrix();
	}

	/**
	 * draw a square plane in the X,Y axis, centered at origin. Include texture
	 * coordinates.
	 * 
	 * @param size
	 *            length of each side
	 * @param segments
	 *            number of segments to divide each side into
	 */
	public static void renderPlane(float size, int segments)
	{
		renderPlane(size, size, segments, segments);
	}

	/**
	 * draw a rectangular plane in the X,Y axis, centered at origin, with the
	 * specified size and number of divisions. Texture will cover entire
	 * rectangle without repeating.
	 * 
	 * @param length
	 *            length of X axis side
	 * @param height
	 *            length of Y axis side
	 * @param segments
	 *            number of segments to divide each side into
	 */
	public static void renderPlane(float length, float height, int length_segments, int height_segments)
	{
		renderPlane(length, height, length_segments, height_segments, 1, 1);
	}

	/**
	 * draw a rectangular plane in the X,Y axis, centered at origin. Include
	 * texture coordinates. Scale the UV coordinates to same proportion as plane
	 * dimensions. Texture will repeat as specified by the tilefactorU and
	 * tilefactorV values. If tilefactor values are 1, the texture will cover
	 * the rectangle without tiling.
	 * 
	 * @param length
	 *            length on X axis
	 * @param depth
	 *            length on Y axis
	 * @param segments
	 *            number of segments to divide each side into
	 */
	public static void renderPlane(float length, float height, int length_segments, int height_segments, float tilefactorU, float tilefactorV)
	{
		float xpos = -length / 2f;
		float ypos = -height / 2f;
		float segsizeL = length / (float) length_segments;
		float segsizeH = height / (float) height_segments;
		float maxDimension = (length > height) ? length : height;
		float uvsegsizeL = (length / maxDimension) / (float) length_segments;
		float uvsegsizeH = (height / maxDimension) / (float) height_segments;
		GL11.glBegin(GL11.GL_QUADS);
		{
			GL11.glNormal3f(0f, 0f, 1f); // plane is facing up the Z axis
			for (int x = 0; x < length_segments; x++, xpos += segsizeL)
			{
				for (int y = 0; y < height_segments; y++, ypos += segsizeH)
				{
					// bottom left
					GL11.glTexCoord2f((x * uvsegsizeL) * tilefactorU, (y * uvsegsizeH) * tilefactorV);
					GL11.glVertex3f(xpos, ypos, 0f);
					// bottom rite
					GL11.glTexCoord2f(((x * uvsegsizeL) + uvsegsizeL) * tilefactorU, (y * uvsegsizeH) * tilefactorV);
					GL11.glVertex3f(xpos + segsizeL, ypos, 0f);
					// top rite
					GL11.glTexCoord2f(((x * uvsegsizeL) + uvsegsizeL) * tilefactorU, ((y * uvsegsizeH) + uvsegsizeH) * tilefactorV);
					GL11.glVertex3f(xpos + segsizeL, ypos + segsizeH, 0f);
					// top left
					GL11.glTexCoord2f((x * uvsegsizeL) * tilefactorU, ((y * uvsegsizeH) + uvsegsizeH) * tilefactorV);
					GL11.glVertex3f(xpos, ypos + segsizeH, 0f);
				}
				ypos = -height / 2f; // reset column position
			}
		}
		GL11.glEnd();
	}

	/**
	 * draw a rectangular plane in the X,Y axis, centered at origin. Include
	 * texture coordinates. Scale the UV coordinates to same proportion as plane
	 * dimensions.
	 * 
	 * @param length
	 *            length on X axis
	 * @param depth
	 *            length on Y axis
	 * @param segments
	 *            number of segments to divide each side into
	 */
	public static void renderPlaneORIG(float length, float height, int length_segments, int height_segments)
	{
		float xpos = -length / 2f;
		float ypos = -height / 2f;
		float segsizeL = length / (float) length_segments;
		float segsizeH = height / (float) height_segments;
		float maxDimension = (length > height) ? length : height;
		float uvsegsizeL = (length / maxDimension) / (float) length_segments;
		float uvsegsizeH = (height / maxDimension) / (float) height_segments;
		GL11.glBegin(GL11.GL_QUADS);
		{
			GL11.glNormal3f(0f, 0f, 1f); // plane is facing up the Z axis
			for (int x = 0; x < length_segments; x++, xpos += segsizeL)
			{
				for (int y = 0; y < height_segments; y++, ypos += segsizeH)
				{
					// bottom left
					GL11.glTexCoord2f(x * uvsegsizeL, y * uvsegsizeH);
					GL11.glVertex3f(xpos, ypos, 0f);
					// bottom rite
					GL11.glTexCoord2f((x * uvsegsizeL) + uvsegsizeL, y * uvsegsizeH);
					GL11.glVertex3f(xpos + segsizeL, ypos, 0f);
					// top rite
					GL11.glTexCoord2f((x * uvsegsizeL) + uvsegsizeL, (y * uvsegsizeH) + uvsegsizeH);
					GL11.glVertex3f(xpos + segsizeL, ypos + segsizeH, 0f);
					// top left
					GL11.glTexCoord2f(x * uvsegsizeL, (y * uvsegsizeH) + uvsegsizeH);
					GL11.glVertex3f(xpos, ypos + segsizeH, 0f);
				}
				ypos = -height / 2f; // reset column position
			}
		}
		GL11.glEnd();
	}

	/**
	 * call the LWJGL Sphere class to draw sphere geometry with texture
	 * coordinates and normals
	 * 
	 * @param facets
	 *            number of divisions around longitude and latitude
	 */
	public static void renderSphere(int facets)
	{
		Sphere s = new Sphere(); // an LWJGL class
		s.setOrientation(GLU.GLU_OUTSIDE); // normals point outwards
		s.setTextureFlag(true); // generate texture coords
		GL11.glPushMatrix();
		{
			GL11.glRotatef(-90f, 1, 0, 0); // rotate the sphere to align the axis vertically
			s.draw(1, facets, facets); // run GL commands to draw sphere
		}
		GL11.glPopMatrix();
	}

	/**
	 * draw a sphere with 48 facets (pretty smooth) with normals and texture
	 * coords
	 */
	public static void renderSphere()
	{
		renderSphere(48);
	}

	/**
	 * Sets glLineWidth() and glPointSize() to the given width. This will affect
	 * geometry drawn using glBegin(GL_LINES), GL_LINE_STRIP, and GL_POINTS. May
	 * only work with widths up to 10 (depends on hardware).
	 */
	public static void setLineWidth(int width)
	{
		GL11.glLineWidth(width);
		GL11.glPointSize(width);
		//GL11.glEnable(GL11.GL_POINT_SMOOTH);
		//GL11.glEnable(GL11.GL_LINE_SMOOTH);
	}

	/**
	 * Set the current color with RGBA floats in range 0-1. The current color is
	 * disabled when lighting is enabled. When lighting is enabled
	 * (glEnable(GL_LIGHTING)) then material colors are in effect and the
	 * current color is ignored.
	 */
	public static void setColor(float R, float G, float B, float A)
	{
		GL11.glColor4f(R, G, B, A);
	}

	/**
	 * Set the current color with RGBA bytes in range 0-255. The current color
	 * is disabled when lighting is enabled. When lighting is enabled
	 * (glEnable(GL_LIGHTING)) then material colors are in effect and the
	 * current color is ignored.
	 */
	public static void setColorB(int R, int G, int B, int A)
	{
		GL11.glColor4ub((byte) R, (byte) G, (byte) B, (byte) A);
	}

	/**
	 * Set the current color to the given RGB or RGBA float array. Floats are in
	 * range 0-1. The current color is disabled when lighting is enabled. When
	 * lighting is enabled (glEnable(GL_LIGHTING)) then material colors are in
	 * effect and the current color is ignored.
	 */
	public static void setColor(float[] rgba)
	{
		if (rgba != null)
		{
			if (rgba.length == 4)
			{
				GL11.glColor4f(rgba[0], rgba[1], rgba[2], rgba[3]);
			}
			else if (rgba.length == 3)
			{
				GL11.glColor4f(rgba[0], rgba[1], rgba[2], 1);
			}
		}
	}

	/**
	 * Enable/disable the color-material setting. When enabled, the glColor()
	 * command will change the current material color. This provides a
	 * convenient and efficient way to change material colors without having to
	 * call glMaterial(). When disabled, the glColor() command functions
	 * normally (has no affect on material colors).
	 * 
	 * @param on
	 *            when true, glColor() will set the current material color
	 */
	public static void setColorMaterial(boolean on)
	{
		if (on)
		{
			// glColor() will change the diffuse and ambient material colors
			GL11.glColorMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT_AND_DIFFUSE);
			GL11.glEnable(GL11.GL_COLOR_MATERIAL);
		}
		else
		{
			// glColor() behaves normally
			GL11.glDisable(GL11.GL_COLOR_MATERIAL);
		}
	}

	//========================================================================
	// Functions to build a character set and draw text strings.
	//
	// Example:
	//           buildFont("Font_tahoma.png");
	//           ...
	//           glPrint(100, 100, 0, "Here's some text");
	//           ...
	//           destroyFont();   // cleanup
	//========================================================================

	static int	fontListBase		= -1;	// Base Display List For The character set
	static int	fontTextureHandle	= -1;	// Texture handle for character set image

	/**
	 * Build a character set from the given texture image.
	 * 
	 * @param charSetImage
	 *            texture image containing 256 characters in a 16x16 grid
	 * @param fontWidth
	 *            how many pixels to allow per character on screen
	 * @see destroyFont()
	 */
	public static boolean buildFont(String charSetImage, int fontWidth)
	{
		// make texture from image
		GLImage textureImg = loadImage(charSetImage);
		if (textureImg == null)
		{
			return false; // image not found
		}
		//pushAttrib();
		fontTextureHandle = makeTexture(textureImg);
		// build character set as call list of 256 textured quads
		buildFont(fontTextureHandle, fontWidth);
		//popAttrib();
		return true;
	}

	/**
	 * Build the character set display list from the given texture. Creates one
	 * quad for each character, with one letter textured onto each quad. Assumes
	 * the texture is a 256x256 image containing every character of the charset
	 * arranged in a 16x16 grid. Each character is 16x16 pixels. Call
	 * destroyFont() to release the display list memory. Should be in ORTHO (2D)
	 * mode to render text (see setOrtho()). Special thanks to NeHe and Giuseppe
	 * D'Agata for the "2D Texture Font" tutorial (http://nehe.gamedev.net).
	 * 
	 * @param charSetImage
	 *            texture image containing 256 characters in a 16x16 grid
	 * @param fontWidth
	 *            how many pixels to allow per character on screen
	 * @see destroyFont()
	 */
	public static void buildFont(int fontTxtrHandle, int fontWidth)
	{
		float factor = 1f / 16f;
		float cx, cy;
		fontListBase = GL11.glGenLists(256); // Creating 256 Display Lists
		for (int i = 0; i < 256; i++)
		{
			cx = (float) (i % 16) / 16f; // X Texture Coord Of Character (0 - 1.0)
			cy = (float) (i / 16) / 16f; // Y Texture Coord Of Character (0 - 1.0)
			GL11.glNewList(fontListBase + i, GL11.GL_COMPILE); // Start Building A List
			GL11.glBegin(GL11.GL_QUADS); // Use A 16x16 pixel Quad For Each Character
			GL11.glTexCoord2f(cx, 1 - cy - factor); // Texture Coord (Bottom Left)
			GL11.glVertex2i(0, 0);
			GL11.glTexCoord2f(cx + factor, 1 - cy - factor); // Texture Coord (Bottom Right)
			GL11.glVertex2i(16, 0);
			GL11.glTexCoord2f(cx + factor, 1 - cy); // Texture Coord (Top Right)
			GL11.glVertex2i(16, 16);
			GL11.glTexCoord2f(cx, 1 - cy); // Texture Coord (Top Left)
			GL11.glVertex2i(0, 16);
			GL11.glEnd(); // Done Building Our Quad (Character)
			GL11.glTranslatef(fontWidth, 0, 0); // Move To The Right Of The Character
			GL11.glEndList(); // Done Building The Display List
		} // Loop Until All 256 Are Built
	}

	/**
	 * Clean up the allocated display lists for the character set.
	 */
	public static void destroyFont()
	{
		if (fontListBase != -1)
		{
			GL11.glDeleteLists(fontListBase, 256);
			fontListBase = -1;
		}
	}

	/**
	 * Render a text string in 2D over the scene, using the character set
	 * created by buildFont().
	 * 
	 * @param x
	 *            screen pixel position of the string
	 * @param y
	 * @param msg
	 *            text string to draw
	 */
	public static void print(int x, int y, String msg)
	{
		print(x, y, msg, 0);
	}

	/**
	 * Render a text string in 2D over the scene, using the character set
	 * created by buildFont().
	 * 
	 * @param x
	 *            screen pixel position of the string
	 * @param y
	 * @param msg
	 *            text string to draw
	 * @param set
	 *            which of the two character sets: 0 or 1
	 */
	public static void print(int x, int y, String msg, int set)
	{
		// if font is not initiallized, try loading default font
		if (fontListBase == -1 || fontTextureHandle == -1)
		{
			if (!buildFont("images/font_tahoma.png", 62))
			{
				Message.err("GLApp.print(): character set has not been created -- see buildFont()");
				return;
			}
		}
		if (msg != null)
		{
			int offset = fontListBase - 32 + (128 * set);
			// preserve current GL settings
			pushAttribOrtho();
			// turn off lighting
			GL11.glDisable(GL11.GL_LIGHTING);
			// enable alpha blending, so character background is transparent
			GL11.glEnable(GL11.GL_BLEND);
			GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
			// enable the charset texture
			GL11.glBindTexture(GL11.GL_TEXTURE_2D, fontTextureHandle);
			// prepare to render in 2D
			setOrthoOn();
			// draw the text
			GL11.glTranslatef(x, y, 0); // Position The Text (in pixel coords)
			for (int i = 0; i < msg.length(); i++)
			{
				GL11.glCallList(offset + msg.charAt(i));
			}
			// restore the original positions and views
			setOrthoOff();
			// restore previous settings
			popAttrib();
		}
	}

	/**
	 * Render a text string in model space, using the character set created by
	 * buildFont().
	 */
	public static void printZ(float x, float y, float z, int set, float scale, String msg)
	{
		int offset;
		if (fontListBase == -1 || fontTextureHandle == -1)
		{
			// font is not initiallized, try this default
			if (!buildFont("images/font_tahoma.png", 12))
			{
				Message.err("GLApp.printZ(): character set has not been created -- see buildFont()");
				return;
			}
		}
		offset = fontListBase - 32 + (128 * set);
		if (msg != null)
		{
			// enable the charset texture
			GL11.glBindTexture(GL11.GL_TEXTURE_2D, fontTextureHandle);
			// draw the text
			GL11.glPushMatrix();
			{
				GL11.glTranslatef(x, y, z); // Position The Text (in pixels coords)
				GL11.glScalef(scale, scale, scale); // make it smaller (arbitrary kludge!!!!)
				for (int i = 0; i < msg.length(); i++)
				{
					GL11.glCallList(offset + msg.charAt(i));
				}
			}
			GL11.glPopMatrix();
		}
	}

	//========================================================================
	// PBuffer functions
	//
	// Pbuffers are offscreen buffers that can be rendered into just like
	// the regular framebuffer.  A pbuffer can be larger than the screen,
	// which allows for the creation of higher resolution images.
	//
	//========================================================================

	/**
	 * Create a Pbuffer for use as an offscreen buffer, with the given width and
	 * height. Use selectPbuffer() to make the pbuffer the context for all
	 * subsequent opengl commands. Use selectDisplay() to make the Display the
	 * context for opengl commands.
	 * <P>
	 * 
	 * @param width
	 * @param height
	 * @return Pbuffer
	 * @see selectPbuffer(), selectDisplay()
	 */
	public static Pbuffer makePbuffer(final int width, final int height)
	{
		Pbuffer pbuffer = null;
		try
		{
			pbuffer = new Pbuffer(width, height, new PixelFormat(24, //bitsperpixel
					8, //alpha
					24, //depth
					8, //stencil
					0), //samples
					null, null);
		}
		catch (LWJGLException e)
		{
			Message.err("GLApp.makePbuffer(): exception " + e);
		}
		return pbuffer;
	}

	/**
	 * Make the pbuffer the current context for opengl commands. All following
	 * gl functions will operate on this buffer instead of the display.
	 * <P>
	 * NOTE: the Pbuffer may be recreated if it was lost since last used. It's a
	 * good idea to use:
	 * 
	 * <PRE>
	 * pbuff = selectPbuffer(pbuff);
	 * </PRE>
	 * 
	 * to hold onto the new Pbuffer reference if Pbuffer was recreated.
	 * 
	 * @param pb
	 *            pbuffer to make current
	 * @return Pbuffer
	 * @see selectDisplay(), makePbuffer()
	 */
	public static Pbuffer selectPbuffer(Pbuffer pb)
	{
		if (pb != null)
		{
			try
			{
				// re-create the buffer if necessary
				if (pb.isBufferLost())
				{
					int w = pb.getWidth();
					int h = pb.getHeight();
					Message.msg("GLApp.selectPbuffer(): Buffer contents lost - recreating the pbuffer");
					pb.destroy();
					pb = makePbuffer(w, h);
				}
				// select the pbuffer for rendering
				pb.makeCurrent();
			}
			catch (LWJGLException e)
			{
				Message.err("GLApp.selectPbuffer(): exception " + e);
			}
		}
		return pb;
	}

	/**
	 * Make the Display the current context for OpenGL commands. Subsequent gl
	 * functions will operate on the Display.
	 * 
	 * @see selectPbuffer()
	 */
	public static void selectDisplay()
	{
		try
		{
			Display.makeCurrent();
		}
		catch (LWJGLException e)
		{
			Message.err("GLApp.selectDisplay(): exception " + e);
		}
	}

	/**
	 * Copy the pbuffer contents to a texture. (Should this use
	 * glCopyTexSubImage2D()? Is RGB the fastest format?)
	 */
	public static void frameCopy(Pbuffer pbuff, int textureHandle)
	{
		GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureHandle);
		GL11.glCopyTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGB, 0, 0, pbuff.getWidth(), pbuff.getHeight(), 0);
	}

	/**
	 * Save the current frame buffer to a PNG image. Same as
	 * screenShot(filename) but the screenshot filename will be automatically
	 * set to <applicationClassName>-<timestamp>.png
	 */
	public static void screenShot()
	{
		screenShot(0, 0, displayMode.getWidth(), displayMode.getHeight(), rootClass.getName() + "-" + makeTimestamp() + ".png");
	}

	/**
	 * Save the current frame buffer to a PNG image. Can also be used with the
	 * PBuffer class to copy large images or textures that have been rendered
	 * into the offscreen pbuffer.
	 */
	public static void screenShot(String imageFilename)
	{
		screenShot(0, 0, displayMode.getWidth(), displayMode.getHeight(), imageFilename);
	}

	/**
	 * Save the current Pbuffer to a PNG image. Same as screenShot(filename) but
	 * the Pbuffer will be saved instead of the framebuffer, and the screenshot
	 * filename will be set to <applicationClassName>-<timestamp>.png NOTE: Have
	 * to call selectPbuffer() before calling this function.
	 */
	public static void screenShot(Pbuffer pb)
	{
		screenShot(0, 0, pb.getWidth(), pb.getHeight(), rootClass.getName() + "_" + makeTimestamp() + ".png");
	}

	/**
	 * Save a region of the current render buffer to a PNG image. If the current
	 * buffer is the framebuffer then this will work as a screen capture. Can
	 * also be used with the PBuffer class to copy large images or textures that
	 * have been rendered into the offscreen pbuffer.
	 * <P>
	 * WARNING: this function hogs memory! Call java with more memory (java
	 * -Xms128m -Xmx128m)
	 * <P>
	 * 
	 * @see selectPbuffer(Pbuffer)
	 * @see selectDisplay()
	 * @see savePixelsToPNG()
	 */
	public static void screenShot(int x, int y, int width, int height, String imageFilename)
	{
		// allocate space for ARBG pixels
		ByteBuffer framebytes = allocBytes(width * height * SIZE_INT);
		int[] pixels = new int[width * height];
		// grab the current frame contents as ARGB ints (BGRA ints reversed)
		GL11.glReadPixels(x, y, width, height, GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, framebytes);
		// copy ARGB data from ByteBuffer to integer array
		framebytes.asIntBuffer().get(pixels, 0, pixels.length);
		// free up this memory
		framebytes = null;
		// flip the pixels vertically and save to file
		GLImage.savePixelsToPNG(pixels, width, height, imageFilename, true);
	}

	/**
	 * Save a ByteBuffer of ARGB pixels to a PNG file. If flipY is true, flip
	 * the pixels on the Y axis before saving.
	 */
	public static void savePixelsToPNG(ByteBuffer framebytes, int width, int height, String imageFilename, boolean flipY)
	{
		if (framebytes != null && imageFilename != null)
		{
			// copy ARGB data from ByteBuffer to integer array
			int[] pixels = new int[width * height];
			framebytes.asIntBuffer().get(pixels, 0, pixels.length);
			// save pixels to file
			GLImage.savePixelsToPNG(pixels, width, height, imageFilename, flipY);
		}
	}

	/**
	 * Save the contents of the current render buffer to a PNG image. This is an
	 * older version of screenShot() that used the default OpenGL GL_RGBA pixel
	 * format which had to be swizzled into an ARGB format. I'm keeping the
	 * function here for reference.
	 * <P>
	 * If the current buffer is the framebuffer then this will work as a screen
	 * capture. Can also be used with the PBuffer class to copy large images or
	 * textures that have been rendered into the offscreen pbuffer.
	 * <P>
	 * WARNING: this function hogs memory! Call java with more memory (java
	 * -Xms128m -Xmx128)
	 * <P>
	 * 
	 * @see selectPbuffer(), selectDisplay()
	 */
	public static void screenShotRGB(int width, int height, String saveFilename)
	{
		// allocate space for RBG pixels
		ByteBuffer framebytes = GLApp.allocBytes(width * height * 3);
		int[] pixels = new int[width * height];
		int bindex;
		// grab a copy of the current frame contents as RGB (has to be UNSIGNED_BYTE or colors come out too dark)
		GL11.glReadPixels(0, 0, width, height, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, framebytes);
		// copy RGB data from ByteBuffer to integer array
		for (int i = 0; i < pixels.length; i++)
		{
			bindex = i * 3;
			pixels[i] = 0xFF000000 // A
					| ((framebytes.get(bindex) & 0x000000FF) << 16) // R
					| ((framebytes.get(bindex + 1) & 0x000000FF) << 8) // G
					| ((framebytes.get(bindex + 2) & 0x000000FF) << 0); // B
		}
		// free up some memory
		framebytes = null;
		// save to file (flip Y axis before saving)
		GLImage.savePixelsToPNG(pixels, width, height, saveFilename, true);
	}

	//========================================================================
	// Stencil functions
	//========================================================================

	/**
	 * clear the stencil buffer
	 */
	public static void clearMask()
	{
		GL11.glClear(GL11.GL_STENCIL_BUFFER_BIT);
	}

	/**
	 * Begin creating a mask. This function turns off the color and depth
	 * buffers so all subsequent drawing will go only into the stencil buffer.
	 * To use: beginMask(1); renderModel(); // draw some geometry endMask();
	 */
	public static void beginMask(int maskvalue)
	{
		// turn off writing to the color buffer and depth buffer
		GL11.glColorMask(false, false, false, false);
		GL11.glDepthMask(false);

		// enable stencil buffer
		GL11.glEnable(GL11.GL_STENCIL_TEST);

		// set the stencil test to ALWAYS pass
		GL11.glStencilFunc(GL11.GL_ALWAYS, maskvalue, 0xFFFFFFFF);
		// REPLACE the stencil buffer value with maskvalue whereever we draw
		GL11.glStencilOp(GL11.GL_REPLACE, GL11.GL_REPLACE, GL11.GL_REPLACE);
	}

	/**
	 * End the mask. Freeze the stencil buffer and activate the color and depth
	 * buffers.
	 */
	public static void endMask()
	{
		// don't let future drawing modify the contents of the stencil buffer
		GL11.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP);

		// turn the color and depth buffers back on
		GL11.glColorMask(true, true, true, true);
		GL11.glDepthMask(true);
	}

	/**
	 * Restrict rendering to the masked area. To use: GLStencil.beginMask(1);
	 * renderModel(); GLStencil.endMask();
	 */
	public static void activateMask(int maskvalue)
	{
		// enable stencil buffer
		GL11.glEnable(GL11.GL_STENCIL_TEST);

		// until stencil test is disabled, only write to areas where the
		// stencil buffer equals the mask value
		GL11.glStencilFunc(GL11.GL_EQUAL, maskvalue, 0xFFFFFFFF);
	}

	/**
	 * turn off the stencil test so stencil has no further affect on rendering.
	 */
	public static void disableMask()
	{
		GL11.glDisable(GL11.GL_STENCIL_TEST);
	}

	/**
	 * Return the stencil buffer value at the given screen position.
	 */
	public static int getMaskValue(int x, int y)
	{
		tmpByte.clear();
		// read the stencil value at the given position, as an unsigned byte, store it in tmpByte
		GL11.glReadPixels(x, y, 1, 1, GL11.GL_STENCIL_INDEX, GL11.GL_UNSIGNED_BYTE, tmpByte);
		return (int) tmpByte.get(0);
	}

	//========================================================================
	// Display list functions
	//
	// Display lists are OpenGL commands that have been optimized and stored
	// into memory on the graphics card.  They greatly improve rendering
	// performance but also "freeze" the geometry, so are not suitable in cases
	// where the geometry has to change dynamically.
	//
	// Display lists have to be deleted from the graphics card when
	// the program exits, or they can accumulate and consume memory.  The
	// function destroyDisplayLists() is called by cleanup() to de-allocate
	// any display lists that were created by these functions.
	//
	//========================================================================
	public static ArrayList<Integer>	displayLists	= new ArrayList<Integer>(); // will hold display list IDs created by beginDisplayList()

	/**
	 * Begin a display list. All following OpenGL geometry commands (up to
	 * endDisplayList()) will be stored in a display list, not drawn to screen.
	 * <P>
	 * To use, create a display list in setup():
	 * 
	 * <PRE>
	 *      int teapotID = beginDisplayList();
	 *      ... // run teapot render code here
	 *      endDisplayList();
	 * </PRE>
	 * 
	 * Then call the display list later in render():
	 * 
	 * <PRE>
	 * callDisplayList(teapotID);
	 * </PRE>
	 * 
	 * @return integer display list id
	 * @see endDisplayList(), callDisplayList(), destroyDisplayList()
	 */
	public static int beginDisplayList()
	{
		int DL_ID = GL11.glGenLists(1); // Allocate 1 new Display List
		GL11.glNewList(DL_ID, GL11.GL_COMPILE); // Start Building A List
		displayLists.add(new Integer(DL_ID)); // save the list ID so we can delete it later (see destroyDisplayLists())
		return DL_ID;
	}

	/**
	 * Finish display list creation. Use this function only after calling
	 * beginDisplayList()
	 * 
	 * @see beginDisplayList()
	 */
	public static void endDisplayList()
	{
		GL11.glEndList();
	}

	/**
	 * Render the geometry stored in a display list. Use this function after
	 * calling beginDisplayList() and endDisplayList() to create a display list.
	 * 
	 * @see beginDisplayList()
	 * @see endDisplayList()
	 */
	public static void callDisplayList(int displayListID)
	{
		GL11.glCallList(displayListID);
	}

	/**
	 * Delete the given display list ID. Frees up resources on the graphics
	 * card.
	 */
	public static void destroyDisplayList(int DL_ID)
	{
		GL11.glDeleteLists(DL_ID, 1);
	}

	/**
	 * Clean up the allocated display lists. Called by cleanUp() when app exits.
	 * 
	 * @see cleanUp();
	 */
	public static void destroyDisplayLists()
	{
		while (displayLists.size() > 0)
		{
			int displaylistID = ((Integer) displayLists.get(0)).intValue();
			GL11.glDeleteLists(displaylistID, 1);
			displayLists.remove(0);
		}
	}

	//========================================================================
	// Native IO Buffer allocation functions
	//
	// These functions create and populate the native buffers used by LWJGL.
	//========================================================================

	public static ByteBuffer allocBytes(int howmany)
	{
		return ByteBuffer.allocateDirect(howmany * SIZE_BYTE).order(ByteOrder.nativeOrder());
	}

	public static IntBuffer allocInts(int howmany)
	{
		return ByteBuffer.allocateDirect(howmany * SIZE_INT).order(ByteOrder.nativeOrder()).asIntBuffer();
	}

	public static FloatBuffer allocFloats(int howmany)
	{
		return ByteBuffer.allocateDirect(howmany * SIZE_FLOAT).order(ByteOrder.nativeOrder()).asFloatBuffer();
	}

	public static DoubleBuffer allocDoubles(int howmany)
	{
		return ByteBuffer.allocateDirect(howmany * SIZE_DOUBLE).order(ByteOrder.nativeOrder()).asDoubleBuffer();
	}

	public static ByteBuffer allocBytes(byte[] bytearray)
	{
		ByteBuffer bb = ByteBuffer.allocateDirect(bytearray.length * SIZE_BYTE).order(ByteOrder.nativeOrder());
		bb.put(bytearray).flip();
		return bb;
	}

	public static IntBuffer allocInts(int[] intarray)
	{
		IntBuffer ib = ByteBuffer.allocateDirect(intarray.length * SIZE_FLOAT).order(ByteOrder.nativeOrder()).asIntBuffer();
		ib.put(intarray).flip();
		return ib;
	}

	public static FloatBuffer allocFloats(float[] floatarray)
	{
		FloatBuffer fb = ByteBuffer.allocateDirect(floatarray.length * SIZE_FLOAT).order(ByteOrder.nativeOrder()).asFloatBuffer();
		fb.put(floatarray).flip();
		return fb;
	}

	public static DoubleBuffer allocDoubles(double[] darray)
	{
		DoubleBuffer fb = ByteBuffer.allocateDirect(darray.length * SIZE_DOUBLE).order(ByteOrder.nativeOrder()).asDoubleBuffer();
		fb.put(darray).flip();
		return fb;
	}

	public static void put(ByteBuffer b, byte[] values)
	{
		b.clear();
		b.put(values).flip();
	}

	public static void put(IntBuffer b, int[] values)
	{
		b.clear();
		b.put(values).flip();
	}

	public static void put(FloatBuffer b, float[] values)
	{
		b.clear();
		b.put(values).flip();
	}

	public static void put(DoubleBuffer b, double[] values)
	{
		b.clear();
		b.put(values).flip();
	}

	/**
	 * copy ints from the given byteBuffer into the given int array.
	 * 
	 * @param b
	 *            source ByteBuffer
	 * @param values
	 *            target integer array, must be same length as ByteBuffer
	 *            capacity/4
	 */
	public static void get(ByteBuffer b, int[] values)
	{
		b.asIntBuffer().get(values, 0, values.length);
	}

	/**
	 * copy ints from the given IntBuffer into the given int array.
	 * 
	 * @param b
	 *            source IntBuffer
	 * @param values
	 *            target integer array, must be same length as IntBuffer
	 */
	public static void get(IntBuffer b, int[] values)
	{
		b.get(values, 0, values.length);
	}

	/**
	 * return the contents of the byteBuffer as an array of ints.
	 * 
	 * @param b
	 *            source ByteBuffer
	 */
	public static int[] getInts(ByteBuffer b)
	{
		int[] values = new int[b.capacity() / SIZE_INT];
		b.asIntBuffer().get(values, 0, values.length);
		return values;
	}

	//========================================================================
	// Misc functions
	//========================================================================
	public static URL	appletBaseURL	= null;
	public static Class	rootClass		= GLApp.class;

	/**
	 * Open the given file and return the InputStream. This function assumes 1)
	 * that we're running an application and the file is in the local
	 * filesystem. If not found, then assume 2) we're in a jar file and look for
	 * the file in the current jar. If not found, then assume 3) we're running
	 * an applet and look for the file relative to the applet code base.
	 * 
	 * @param filename
	 *            to open
	 */
	public static InputStream getInputStream(String filename)
	{
		InputStream in = null;

		// 1) look for file in local filesystem
		try
		{
			in = new FileInputStream(filename);
		}
		catch (IOException ioe)
		{
			Message.msg("GLApp.getInputStream (" + filename + "): " + ioe);
			if (in != null)
			{
				try
				{
					in.close();
				}
				catch (Exception e)
				{}
				in = null;
			}
		}
		catch (Exception e)
		{
			Message.msg("GLApp.getInputStream (" + filename + "): " + e);
		}

		// 2) if couldn't open file, look in jar
		if (in == null && rootClass != null)
		{
			// NOTE: class.getResource() looks for files relative to the folder that the class is in.
			// ideally the class will be an application in the root of the installation, see setRootClass().
			URL u = null;
			if (filename.startsWith("."))
			{ // remove leading . ie. "./file"
				filename = filename.substring(1);
			}
			try
			{
				u = rootClass.getResource(filename);
			}
			catch (Exception ue)
			{
				Message.msg("GLApp.getInputStream(): Can't find resource: " + ue);
			}
			//msg("GLApp.getInputStream (" +filename+ "): try jar resource url=" + u);
			if (u != null)
			{
				try
				{
					in = u.openStream();
				}
				catch (Exception e)
				{
					Message.msg("GLApp.getInputStream (" + filename + "): Can't load from jar: " + e);
				}
			}

			// 3) try loading file from applet base url
			if (in == null && appletBaseURL != null)
			{
				try
				{
					u = new URL(appletBaseURL, filename);
				}
				catch (Exception ue)
				{
					Message.msg("GLApp.getInputStream(): Can't make applet base url: " + ue);
				}
				//msg("GLApp.getInputStream (" +filename+ "): try applet base url=" + u);
				try
				{
					in = u.openStream();
				}
				catch (Exception e)
				{
					Message.msg("GLApp.getInputStream (" + filename + "): Can't load from applet base URL: " + e);
				}
			}
		}
		return in;
	}

	/**
	 * Return an array of bytes read from an InputStream. Reads all bytes until
	 * the end of stream. Can read an arbitrary number of bytes. NOTE: Does not
	 * close the inputStream!
	 */
	public static byte[] getBytesFromStream(InputStream is)
	{
		int chunkSize = 1024;
		int totalRead = 0;
		int num = 0;
		byte[] bytes = new byte[chunkSize];
		ArrayList<byte[]> byteChunks = new ArrayList();

		// Read the bytes in chunks of 1024
		try
		{
			while ((num = is.read(bytes)) >= 0)
			{
				byteChunks.add(bytes);
				bytes = new byte[chunkSize];
				totalRead += num;
			}
		}
		catch (IOException ioe)
		{
			Message.err("GLApp.getBytesFromStream(): IOException " + ioe);
		}

		int numCopied = 0;
		bytes = new byte[totalRead];

		// copy byte chunks to byte array (last chunk may be partial)
		while (byteChunks.size() > 0)
		{
			byte[] byteChunk = (byte[]) byteChunks.get(0);
			int copylen = (totalRead - numCopied > chunkSize) ? chunkSize : (totalRead - numCopied);
			System.arraycopy(byteChunk, 0, bytes, numCopied, copylen);
			byteChunks.remove(0);
			numCopied += copylen;
		}

		Message.msg("getBytesFromStream() read " + numCopied + " bytes.");

		return bytes;
	}

	/**
	 * Return an array of bytes read from a file.
	 */
	public static byte[] getBytesFromFile(String filename)
	{
		InputStream is = getInputStream(filename);
		byte[] bytes = getBytesFromStream(is);
		try
		{
			is.close();
		}
		catch (IOException ioe)
		{
			Message.err("GLApp.getBytesFromFile(): IOException " + ioe);
		}
		return bytes;
	}

	/**
	 * Return a String array containing the path portion of a filename
	 * (result[0]), and the fileame (result[1]). If there is no path, then
	 * result[0] will be "" and result[1] will be the full filename.
	 */
	public static String[] getPathAndFile(String filename)
	{
		String[] pathAndFile = new String[2];
		Matcher matcher = Pattern.compile("^.*/").matcher(filename);
		if (matcher.find())
		{
			pathAndFile[0] = matcher.group();
			pathAndFile[1] = filename.substring(matcher.end());
		}
		else
		{
			pathAndFile[0] = "";
			pathAndFile[1] = filename;
		}
		return pathAndFile;
	}

	/**
	 * Hold onto this Class for later class.getResource() calls (to load
	 * resources from JAR files, see getInputStream()) and also to get class
	 * name for use in screenshot filenames (see screenShot()).
	 * <P>
	 * To load files from a jar we need to access a class in the root folder of
	 * the installation. It's not good to use GLApp.class because that class is
	 * in the glapp package folder, and the getResource() function will not find
	 * model, image and sound files because they're a level higher in the folder
	 * tree. Below we call this.getClass() to record the class of the
	 * application that subclasses GLApp, ie. assume we create an app MyGame
	 * that extends GLApp, and MyGame.class is in the root folder of the
	 * installation:
	 * 
	 * <PRE>
	 *      MyGame.class
	 *      models (folder)
	 *      images (folder)
	 *      sounds (folder)
	 * </PRE>
	 * 
	 * In this case setRootClass() will set the rootClass to MyGame. If MyGame
	 * and subfolders are packaged in a jar file, then getInputStream() should
	 * be able to do a rootClass.getResource("models/some_model.obj") and
	 * correctly retrieve the file from the JAR.
	 * <P>
	 * 
	 * @see getInputStream()
	 */
	public void setRootClass()
	{
		rootClass = this.getClass();
	}

	/**
	 * make a time stamp for filename
	 * 
	 * @return a string with format "YYYYMMDD-hhmmss"
	 */
	public static String makeTimestamp()
	{
		Calendar now = Calendar.getInstance();
		int year = now.get(Calendar.YEAR);
		int month = now.get(Calendar.MONTH) + 1;
		int day = now.get(Calendar.DAY_OF_MONTH);
		int hours = now.get(Calendar.HOUR_OF_DAY);
		int minutes = now.get(Calendar.MINUTE);
		int seconds = now.get(Calendar.SECOND);
		String datetime = "" + year + (month < 10 ? "0" : "") + month + (day < 10 ? "0" : "") + day + "-" + (hours < 10 ? "0" : "") + hours + (minutes < 10 ? "0" : "") + minutes
				+ (seconds < 10 ? "0" : "") + seconds;
		return datetime;
	}

	/**
	 * Return a random floating point value between 0 and 1
	 */
	public static float random()
	{
		return (float) Math.random();
	}

	/**
	 * Return a random floating point value between 0 and upperbound (not
	 * including upperbound)
	 */
	public static float random(float upperbound)
	{
		return (float) (Math.random() * (double) upperbound);
	}

	/**
	 * Return a random integer value between 0 and upperbound (not including
	 * upperbound)
	 */
	public static int random(int upperbound)
	{
		return (int) (Math.random() * (double) upperbound);
	}

	/**
	 * Round a float value to the nearest int.
	 */
	public static int round(float f)
	{
		return Math.round(f);
	}

	/**
	 * Return true if the OpenGL context supports the given OpenGL extension.
	 */
	public static boolean extensionExists(String extensionName)
	{
		if (OpenGLextensions == null)
		{
			String[] GLExtensions = GL11.glGetString(GL11.GL_EXTENSIONS).split(" ");
			OpenGLextensions = new Hashtable<String, String>();
			for (int i = 0; i < GLExtensions.length; i++)
			{
				OpenGLextensions.put(GLExtensions[i].toUpperCase(), "");
			}
		}
		return (OpenGLextensions.get(extensionName.toUpperCase()) != null);
	}

	/**
	 * Find a method in the given class with the given method name. Assumes the
	 * method takes no parameters. The returned Method can be executed later
	 * using invoke() (similar to a callback function in C/C++).
	 * <P>
	 * NOTE: method invocation is very fast for methods that take no parameters.
	 * If the method takes parameters then invoking is much slower than calling
	 * the function directly through code. For this reason and for simplicity I
	 * assume there are no parameters on the function.
	 * 
	 * @param object
	 *            object that has the method we want to invoke
	 * @param methodName
	 *            name of function that we want to invoke
	 * @return the Method object
	 * @see invoke()
	 */
	public static Method method(Object object, String methodName)
	{
		Method M = null;
		try
		{
			// Look for a method with the given name and no parameters
			M = object.getClass().getMethod(methodName, null);
		}
		catch (Exception e)
		{
			Message.err("GLApp.method(): Can't find method (" + methodName + ").  " + e);
		}
		return M;
	}

	/**
	 * Similar to the static method() function, this looks for the method in the
	 * GLApp class (or it's subclass).
	 * 
	 * @param methodName
	 *            name of function that we want to invoke
	 * @return the Method object
	 * @see invoke()
	 */
	public Method method(String methodName)
	{
		return method(this, methodName);
	}

	/**
	 * Execute a method on the given object. Assumes the method takes no
	 * parameters. Useful as a callback function.
	 * 
	 * @param object
	 *            (the object to call the method on)
	 * @param method
	 *            (the method that will be executed)
	 * @see method()
	 */
	public static void invoke(Object object, Method method)
	{
		if (object != null && method != null)
		{
			try
			{
				// Call the method with this object as the argument!
				method.invoke(object, null);
			}
			catch (Exception e)
			{
				// Error handling
				System.err.println("GLApp.invoke(): couldn't invoke method " + method.getName() + " on object " + object.getClass().getName());
			}
		}
	}

	/**
	 * Similar to the static invoke() function, this execute a method on the
	 * GLApp class or subclass. Assumes the method takes no parameters. Useful
	 * as a callback function.
	 * 
	 * @param method
	 *            (the method that will be executed)
	 * @see method()
	 */
	public void invoke(Method method)
	{
		if (method != null)
		{
			try
			{
				// Call the method with this object as the argument!
				method.invoke(this, null);
			}
			catch (Exception e)
			{
				// Error handling
				System.err.println("GLApp.invoke(): couldn't invoke method " + method.getName() + " on object " + this.getClass().getName());
			}
		}
	}
}